Now I've been trying to implement parallax mapping for a short while now, and I'm not quite sure if the result is right. The result (look at the plane):
Now there I have shown you the result of Diffuse, Parallax, Parallax with Normal Mapping rendering, and a blank space of course!
Now to some code:
(Relevant) Vertex Shader:
float binormal = cross(tangent,normal);
float3x3 tbnmatrix = transpose(float3x3(tangent,binormal,normal));
output.toeyetangent = mul((eyepos - output.position),tbnmatrix);
(Relevant) Pixel Shader:
float height = parallaxmap.Sample(ss, texCoord);
float3 toeyetangent = normalize(input.toeyetangent);
float2 offset = toeyetangent.xy*height*0.04f;
texCoord += offset;
And If you wan't, the full shader:
cbuffer ConstantObjectBuffer : register (b0)
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
float4x4 rotation;
float4 ambientcol;
float texture_repeat;
float3 tr_sace;
float4 clipColor;
float3 _instancePos;
float inSPACE;
};
cbuffer ConstantFrameBuffer : register (b1)
{
float4 lightvec;
float4 lightcol;
float time;
float _wireframe;
float2 wffspace;
matrix tmat;
matrix svp;
};
cbuffer UberData : register (b2)
{
float _diffuse;
float _texture;
float _opacityEnable;
float _directionall;
float _bumpmap;
float _fog;
float _wind;
float _pixelDisorter;
float _disorterAmount;
float _specular;
float _doClip;
float _terrain;
float _instance;
float _selected;
float _windspeed;
float _windamount;
float _particle;
float _shadows;
float _envreflect;
float _invDoClip;
float _parallaxMapping;
float3 ssspace;
};
cbuffer ConstantMatrixBuffer : register (b3)
{
float4x4 world;
float4x4 view;
float4x4 projection;
float4 eyepos;
float3 cforward;
float _space;
};
cbuffer ConstantFogBuffer : register (b4)
{
float FogStart;
float FogEnd;
float2 __space;
float3 FogColor;
float __space2;
};
cbuffer ConstantSpecularBuffer : register (b5)
{
float specularPower;
float3 ____space;
float4 specularColor;
};
cbuffer ConstantTerrainBuffer : register (b6)
{
float t_nchannels;
float3 t_space;
float4 slopeActivation;
};
cbuffer ConstantParticleBuffer : register (b7)
{
float particle_pastTime;
float particle_lifeTime;
float particle_fallOff;
float _cpbspace;
};
cbuffer ShadowBuffer : register (b8)
{
matrix sWorld;
matrix lightViewMatrix;
matrix lightProjectionMatrix;
float3 sLightPos;
}
// Meshes
Texture2D txt : register(t0); // base
Texture2D txt2 : register(t1); // bump
Texture2D parallaxmap : register(t12); // parallax
Texture2D txt3 : register(t2); // opacity
TextureCube env : register (t11);
//Terrain
Texture2D terrChannel1 : register(t3);
Texture2D terrChannel2 : register(t4);
Texture2D terrChannel3 : register(t5);
Texture2D terrChannel4 : register(t6);
Texture2D gBuffer_Normals : register(t7);
Texture2D gBuffer_Position : register(t8);
Texture2D gBuffer_Random : register(t9);
Texture2D gBuffer_Shadows : register(t10);
sampler shadowss =
sampler_state
{
Texture = <gBuffer_Shadows>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
SamplerState ss;
sampler NormalMapSampler = sampler_state
{
Texture = <txt2>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
struct VOut
{
float4 worldPos : POSITION;
float3 normal : NORMAL;
float3 Specnormal : NORMAL2;
float2 texcoord : TEXCOORD;
float4 tangent : TANGENT;
float4 position : SV_POSITION;
float3 WorldNormal : TEXCOORD2;
float Depth : FOG;
float4 color : COLOR;
float3 View : VIEWD;
float4 lightViewPosition : TEXCOORD5;
float3 L : TEXCOORD6;
float3 N : TEXCOORD7;
float3 lightPos : TEXCOORD8;
float3 toeyetangent : EYET;
};
VOut VShader(float4 position : POSITION, float4 normal : NORMAL, float2 texcoord : TEXCOORD, float4 tangent : TANGENT, float3 instancePosition : INSTANCEPOS)
{
VOut output;
if (_wind == 1)
{
position.x += sin(time+position.x*10);
position.y += cos(time+position.y*10);
position.z += sin(time+position.y*10);
// Parameters controlling the wind effect.
float3 WindDirection = float3(1, 0, 0);
float WindWaveSize = 0.1;
float WindRandomness = 1;
float WindSpeed = _windspeed;
float WindAmount = _windamount;
// Work out how this vertex should be affected by the wind effect.
float waveOffset = dot(position, WindDirection) * WindWaveSize;
waveOffset += 2 * WindRandomness;
// Wind makes things wave back and forth in a sine wave pattern.
float wind = sin(time * WindSpeed + waveOffset) * WindAmount;
// But it should only affect the top two vertices of the billboard!
wind *= (1 - texcoord.y);
position += float4(WindDirection * wind, position.w);
}
if (_instance == 1)
{
position.x += instancePosition.x;
position.y += instancePosition.y;
position.z += instancePosition.z;
}
position.w = 1.0f;
// Calculate the position of the vertex against the world, view, and projection matrices.
output.position = mul(position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);
output.worldPos = mul(position, worldMatrix);
output.WorldNormal = normalize(mul(normal, worldMatrix));
output.color = float4(0,0,0,1);
// set the ambient light
if (_diffuse == 1)
{
output.color += ambientcol;
}
if (_texture == 1)
{
output.texcoord = texcoord;
}
// Stuff for dir and bump
if (_directionall == 1)
{
float4 norm = normalize(mul(rotation, normal));
float diffusebrightness = saturate(dot(norm, lightvec));
output.color += lightcol * diffusebrightness;
}
if (_bumpmap == 1)
{
output.normal = normal;
output.tangent = tangent;
}
float3 Cnormal = normalize(mul(normal, worldMatrix));
output.Specnormal = Cnormal;
output.View = normalize(eyepos - output.worldPos);
// SHADOW
if (_shadows == 1)
{
// Calculate the position of the vertice as viewed by the light source.
output.lightViewPosition = mul(position, worldMatrix);
output.lightViewPosition = mul(output.lightViewPosition, lightViewMatrix);
output.lightViewPosition = mul(output.lightViewPosition, lightProjectionMatrix);
// Calculate the normal vector against the world matrix only.
output.normal = mul(normal, (float3x3)worldMatrix);
// Normalize the normal vector.
output.normal = normalize(output.normal);
// Calculate the position of the vertex in the world.
float4 worldPosition = mul(position, worldMatrix);
// Determine the light position based on the position of the light and the position of the vertex in the world.
output.lightPos = sLightPos - worldPosition.xyz;
// Normalize the light position vector.
output.lightPos = normalize(output.lightPos);
}
// END
if (_parallaxMapping == 1)
{
float binormal = cross(tangent,normal);
float3x3 tbnmatrix = transpose(float3x3(tangent,binormal,normal));
output.toeyetangent = mul((eyepos - output.position),tbnmatrix);
}
// Final stuff
output.Depth = output.position.z;
output.color.a = 1;
return output;
}
// SSAO Operations
float3 getPosition(in float2 uv)
{
return gBuffer_Position.Sample(ss, uv).xyz;
}
float3 getNormal(in float2 uv)
{
return normalize(gBuffer_Normals.Sample(ss, uv).xyz * 2.0f - 1.0f);
}
float2 getRandom(in float2 uv)
{
return normalize(gBuffer_Random.Sample(ss, float2(800,600) * uv / float2(64,64)).xy * 2.0f - 1.0f);
}
float doAmbientOcclusion(in float2 tcoord,in float2 uv, in float3 p, in float3 cnorm)
{
float3 diff = getPosition(tcoord + uv) - p;
const float3 v = normalize(diff);
const float d = length(diff)*2; //scale
return max(0.0,dot(cnorm,v)-5)*(1.0/(1.0+d))*1; // Intensity
}
float4 PShader(VOut input) : SV_TARGET
{
// If it's wireframe, skip everything else
if (_wireframe == 1)
{
return float4(1,1,1,1); // we may have to do something with this, not sure yet...
}
float4 color = input.color;
float4 spec = float4(0,0,0, 1);
float4 tex = float4(0,0,0, 1);
float4 bump = float4(0,0,0, 1);
input.texcoord.xy *= texture_repeat;
float2 texCoord = input.texcoord;
if (_texture == 1)
{
if (_parallaxMapping == 1)
{
float height = parallaxmap.Sample(ss, texCoord);
float3 toeyetangent = normalize(input.toeyetangent);
float2 offset = toeyetangent.xy*height*0.04f;
texCoord += offset;
}
if (_pixelDisorter == 1)
{
// Distortion factor
float NoiseX = _disorterAmount * (time/1000) * sin(input.texcoord.x * input.texcoord.y+time/1000);
NoiseX=fmod(NoiseX,8) * fmod(NoiseX,4);
// Use our distortion factor to compute how much it will affect each
// texture coordinate
float DistortX = fmod(NoiseX,5);
float DistortY = fmod(NoiseX,5+0.002);
// Create our new texture coordinate based on our distortion factor
texCoord = float2(DistortX,DistortY);
}
if (!_bumpmap)
{
float4 txtColor = txt.Sample(ss, texCoord);
color *= txtColor;
}
else if (_bumpmap == 1)
{
float4 Color = txt.Sample(ss, texCoord);
float3 N = (2.0 * (txt2.Sample(ss, texCoord))) - 1.0;
// diffuse
float D = saturate(dot(N, lightvec));
// reflection
float3 R = normalize(2 * D * N - lightvec);
// specular
float S = pow(saturate(dot(R, texCoord*input.tangent)), 2);
const float4 Ambient = float4(0.3, 0.3, 0.3, 1.0);
color *= Color*Ambient + Color * D + Color*S;
}
if (_doClip == 1)
{
if (_invDoClip == 1)
{
color.a = 1.0f - saturate(txt.Sample(ss, texCoord));
}
else
{
color.a = saturate(txt.Sample(ss, texCoord));
}
}
}
if (_specular == 1)
{
float4 normal = float4(input.Specnormal, 1.0);
float4 diffuse = saturate(dot(-lightvec,normal));
float4 reflect = normalize(2*diffuse*normal-lightvec);
float4 specular = pow(saturate(dot(reflect,input.View)),15);
color += float4(1,1,1,1)*specular;
}
if (_opacityEnable == 1)
{
float4 opacity = saturate(txt3.Sample(ss, texCoord));
color.a *= opacity;
}
if (_terrain == 1)
{
float4 grassColor;
float4 slopeColor;
float4 rockColor;
float slope;
float blendAmount;
float4 textureColor;
grassColor = terrChannel1.Sample(ss, input.texcoord);
slopeColor = terrChannel2.Sample(ss, input.texcoord);
rockColor = terrChannel3.Sample(ss, input.texcoord);
// Calculate the slope of this point.
slope = 1.0f - input.normal.y;
// Determine which texture to use based on height.
if(slope < 0.2)
{
blendAmount = slope / 0.2;
textureColor = lerp(grassColor, slopeColor, blendAmount);
}
if((slope < 0.7) && (slope >= 0.2))
{
blendAmount = (slope - 0.2) * (1.0f / (0.7 - 0.2));
textureColor = lerp(slopeColor, rockColor, blendAmount);
}
if(slope >= 0.7)
{
textureColor = rockColor;
}
color *= textureColor;
}
if (_particle == 1)
{
float range = particle_lifeTime-particle_fallOff;
if (particle_pastTime > range)
{
color.a *= saturate(1.0f - (1.0f/( (particle_lifeTime-(particle_lifeTime - particle_fallOff)) / (particle_pastTime-(particle_lifeTime - particle_fallOff)) )));
color.a -= 1.0f/(distance(eyepos.xyz, input.Depth)) / 50.0f;
}
}
if (_shadows == 1)
{
float bias;
float2 projectTexCoord;
float depthValue;
float lightDepthValue;
float lightIntensity;
float4 textureColor;
// Set the bias value for fixing the floating point precision issues.
bias = 0.0001f;
// Calculate the projected texture coordinates.
projectTexCoord.x = input.lightViewPosition.x / input.lightViewPosition.w / 2.0f + 0.5f;
projectTexCoord.y = -input.lightViewPosition.y / input.lightViewPosition.w / 2.0f + 0.5f;
// Determine if the projected coordinates are in the 0 to 1 range. If so then this pixel is in the view of the light.
if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
{
// Sample the shadow map depth value from the depth texture using the sampler at the projected texture coordinate location.
depthValue = gBuffer_Shadows.Sample(ss, projectTexCoord).r;
// Calculate the depth of the light.
lightDepthValue = input.lightViewPosition.z / input.lightViewPosition.w;
// Subtract the bias from the lightDepthValue.
lightDepthValue = lightDepthValue - bias;
// Compare the depth of the shadow map value and the depth of the light to determine whether to shadow or to light this pixel.
// If the light is in front of the object then light the pixel, if not then shadow this pixel since an object (occluder) is casting a shadow on it.
if(lightDepthValue < depthValue)
{
// Calculate the amount of light on this pixel.
lightIntensity = saturate(dot(input.normal, -lightvec));
if(lightIntensity > 0.0f)
{
// Determine the final diffuse color based on the diffuse color and the amount of light intensity.
color += (ambientcol * lightIntensity) * color.a;
// Saturate the final light color.
color = saturate(color);
}
}
else
color.rgb *= 0.3f;
}
}
if (_fog == 1)
{
float l = saturate((input.Depth - FogStart) / (FogEnd - FogStart));
color = float4(lerp(color,FogColor, l), color.a);
}
if (_selected == 1)
{
color += float4(0, 0, 0.2, 0);
}
color.a = saturate(color.a);
return color;
}
Thanks!