读书人

3DShader之位移贴图(Displacement Map

发布时间: 2013-10-08 16:38:32 作者: rapoo

3DShader之移位贴图(Displacement Mapping)

我们知道法线贴图是只是改了物体的法线属性,用来计算光照,但是并没有改变物体本身的网格.但是移位贴图就不一样了,它会移动物体的顶点.我用移位贴图做了个海洋,好了,上了图再讲:





3DShader之位移贴图(Displacement Mapping)3DShader之位移贴图(Displacement Mapping)

注意看海的边缘的顶点,已经实现了移动


最后,添加了一个笛卡尔转球形坐标的函数将其转为球形坐标,到时候我会提供球形版本的源码,如果需要平面的只需要在shader将调用这个函数的语句注释掉即可.


3DShader之位移贴图(Displacement Mapping)


好了,不嗦了,困得不行了!


类似于法线贴图,移位贴图的每一个纹素中存储了一个向量,这个向量代表了对应顶点的位移。


注意,此处的纹素并不是与像素一一对应,而是与顶点一一对应,因此,纹理的纹素个数与网格的顶点个数是相等的。为什么必须相等,源代码里写

了原因的

在VS阶段,需要获取每个顶点对应的纹素中的位移向量. 注意只有3.0版本以上的vs才支持在VS阶段获取纹理数据,之前的版本只有ps才能获取纹理数据


好了,讲下这个项目的大概原理

这个项目,有两张移位纹理和两张法线纹理,目的就是为了进一步随机化.所以跟这里的实质内容关系不大,我会把它当成一张来讲

移位纹理可以存储需要移位的向量,而我们这里只存储了一个r通道,g,b通道都为1,所以我们的移位纹理储存的是[0,255]的随机值,注意移位纹理是128*128的,其实就是一个128*128的高度图.用的就是这个高度图去波动128*128网格的顶点的y值,当然采样高度图的坐标是移动的.

移动了顶点的y坐标,前面我们在法线贴图也提到过可以通过高度图生成法线纹理,这里一样,通过变化后的Y坐标也可以生成这个点的法向量.

这个法向量就用来算光照.当然这样算出来的光照很粗略,如下图所示:

3DShader之位移贴图(Displacement Mapping)

所以,法线贴图又来了,这个是为了增加细节效果的,跟移位没有点关系,具体的方法是:将摄像机,灯光全转换到顶点的切线空间,然后通过纹理坐标对法线纹理进行采样,然后再以这三个变量来算光照.注意项目中的采样法线纹理的坐标经过缩放的,它重复了8次.

加上细节后:

3DShader之位移贴图(Displacement Mapping)



好了,贴源代码

/*------------------------3D_Shader_DisplacementMapping.cpp -- achieve displacement mapping(c) Seamanj.2013/9/2------------------------*/struct Material{float4 ambient;float4 diffuse;float4 specular;float  specularPower;};struct DirectionalLight{float4 ambient;float4 diffuse;float4 specular;float3 directionInWorld;  };uniform extern float4x4 g_mWorld;uniform extern float4x4 g_mWorldInv;uniform extern float4x4 g_mWorldViewProj;uniform extern float3   g_eyePositionInWorld;uniform extern DirectionalLight g_structDirectionalLight;uniform extern Material g_structMaterial;// Two normal maps and displacement maps.uniform extern texture  g_texNormalMap1;uniform extern texture  g_texNormalMap2;uniform extern texture  g_texDisplacementMap1;uniform extern texture  g_texDisplacementMap2;// Texture coordinate offset vectors for scrolling// normal maps and displacement maps.uniform extern float2   g_normalOffset1;uniform extern float2   g_normalOffset2;uniform extern float2   g_DisplacementOffset1;uniform extern float2   g_DisplacementOffset2;// User-defined scaling factors to scale the heights// sampled from the displacement map into a more convenient// range.uniform extern float2 g_scaleHeights;// Space between grids in x,z directions in local space// used for finite differencing.uniform extern float2 g_delta;// Shouldn't be hardcoded, but ok for demo.static const float DISPLACEMENTMAP_SIZE = 128.0f;static const float DISPLACEMENTMAP_DELTA   = 1.0f / DISPLACEMENTMAP_SIZE;sampler g_samNormalMap1 = sampler_state{Texture = <g_texNormalMap1>;MinFilter = ANISOTROPIC;MaxAnisotropy = 12;MagFilter = LINEAR;MipFilter = LINEAR;AddressU  = WRAP;    AddressV  = WRAP;};sampler g_samNormalMap2 = sampler_state{Texture = <g_texNormalMap2>;MinFilter = ANISOTROPIC;MaxAnisotropy = 12;MagFilter = LINEAR;MipFilter = LINEAR;AddressU  = WRAP;    AddressV  = WRAP;};sampler g_samDisplacementMap1 = sampler_state{Texture = <g_texDisplacementMap1>;MinFilter = POINT;MagFilter = POINT;MipFilter = POINT;AddressU  = WRAP;    AddressV  = WRAP;};sampler g_samDisplacementMap2 = sampler_state{Texture = <g_texDisplacementMap2>;MinFilter = POINT;MagFilter = POINT;MipFilter = POINT;AddressU  = WRAP;    AddressV  = WRAP;};struct OutputVS{    float4 posInHomogeneous : POSITION0;    float3 toEyeInTangent    : TEXCOORD0;    float3 lightDirectionInTangent : TEXCOORD1;    float2 texcoord1 : TEXCOORD2;    float2 texcoord2 : TEXCOORD3;};float3 CartesianToSpherical(float3 cartesianCoord,float radius){float gamma,theta;gamma = cartesianCoord.x / (g_delta.x * 127 / 2) * 3.14159265;theta = (g_delta.y * 127 / 2 - cartesianCoord.z) / (g_delta.y * 127 / 2) * 3.14159265 / 2;float sinGamma,cosGamma,sinTheta,cosTheta;sincos(gamma, sinGamma, cosGamma);sincos(theta, sinTheta, cosTheta);float3 sphericalCoord;radius = radius  + cartesianCoord.y ;sphericalCoord.x = radius * sinTheta * cosGamma;sphericalCoord.z = radius * sinTheta * sinGamma;sphericalCoord.y = radius * cosTheta;return sphericalCoord;}float DoDisplacementMapping(float2 texCoord1, float2 texCoord2){// Transform to texel space    float2 texelPos = DISPLACEMENTMAP_SIZE * texCoord1;            // Determine the lerp amounts.               float2 lerps = frac(texelPos);    float height1[4];//由于移位纹理显示青色(即B,G通道都为1,所以应该是R通道存储的是高度height1[0] = tex2Dlod(g_samDisplacementMap1, float4(texCoord1, 0.0f, 0.0f)).r;height1[1] = tex2Dlod(g_samDisplacementMap1, float4(texCoord1, 0.0f, 0.0f)+float4(DISPLACEMENTMAP_DELTA, 0.0f, 0.0f, 0.0f)).r;height1[2] = tex2Dlod(g_samDisplacementMap1, float4(texCoord1, 0.0f, 0.0f)+float4(0.0f, DISPLACEMENTMAP_DELTA, 0.0f, 0.0f)).r;height1[3] = tex2Dlod(g_samDisplacementMap1, float4(texCoord1, 0.0f, 0.0f)+float4(DISPLACEMENTMAP_DELTA, DISPLACEMENTMAP_DELTA, 0.0f, 0.0f)).r;//这里取出来的值范围在[0,1]之内// Filter displacement map:float h1 = lerp( lerp( height1[0], height1[1], lerps.x ),                     lerp( height1[2], height1[3], lerps.x ),                     lerps.y );texelPos = DISPLACEMENTMAP_SIZE * texCoord2;lerps    = frac(texelPos);float height2[4];height2[0] = tex2Dlod(g_samDisplacementMap2, float4(texCoord2, 0.0f, 0.0f)).r;height2[1] = tex2Dlod(g_samDisplacementMap2, float4(texCoord2, 0.0f, 0.0f)+float4(DISPLACEMENTMAP_DELTA, 0.0f, 0.0f, 0.0f)).r;height2[2] = tex2Dlod(g_samDisplacementMap2, float4(texCoord2, 0.0f, 0.0f)+float4(0.0f, DISPLACEMENTMAP_DELTA, 0.0f, 0.0f)).r;height2[3] = tex2Dlod(g_samDisplacementMap2, float4(texCoord2, 0.0f, 0.0f)+float4(DISPLACEMENTMAP_DELTA, DISPLACEMENTMAP_DELTA, 0.0f, 0.0f)).r;// Filter displacement map:float h2 = lerp( lerp( height2[0], height2[1], lerps.x ),                     lerp( height2[2], height2[3], lerps.x ),                     lerps.y );                   // Sum and scale the sampled heights.      return g_scaleHeights.x * h1 + g_scaleHeights.y * h2;}OutputVS myVertexEntry(float3 positionInLocal : POSITION0,                  float2 scaledTexCoord     : TEXCOORD0,//供法线纹理使用                 float2 normalizedTexCoord : TEXCOORD1)//供移位纹理使用{    // Zero out our output.OutputVS outVS = (OutputVS)0;// Scroll vertex texture coordinates to animate waves.float2 DisplacementTexCoord1 = normalizedTexCoord + g_DisplacementOffset1;float2 DisplacementTexCoord2 = normalizedTexCoord + g_DisplacementOffset2;// Set y-coordinate of water grid vertices based on displacement mapping.positionInLocal.y = DoDisplacementMapping(DisplacementTexCoord1, DisplacementTexCoord2);// Estimate TBN-basis using finite differencing in local space.  float left = DoDisplacementMapping(DisplacementTexCoord1 + float2(DISPLACEMENTMAP_DELTA, 0.0f),                         DisplacementTexCoord2 + float2(0.0f, DISPLACEMENTMAP_DELTA));float front = DoDisplacementMapping(DisplacementTexCoord1 + float2(DISPLACEMENTMAP_DELTA, 0.0f),                         DisplacementTexCoord2 + float2(0.0f, DISPLACEMENTMAP_DELTA));                          float3x3 TBN;                       TBN[0] = normalize(float3(1.0f, (left - positionInLocal.y)/g_delta.x, 0.0f)); //TangentTBN[1] = normalize(float3(0.0f, (front - positionInLocal.y)/g_delta.y, -1.0f));//BinormalTBN[2] = normalize(cross(TBN[0], TBN[1]));//Normal// Matrix transforms from object space to tangent space.float3x3 toTangentSpace = transpose(TBN);// Transform eye position to local space.float3 eyePositionInLocal = mul(float4(g_eyePositionInWorld, 1.0f), g_mWorldInv).xyz;// Transform to-eye vector to tangent space.float3 toEyeInLocal = eyePositionInLocal - positionInLocal;outVS.toEyeInTangent = mul(toEyeInLocal, toTangentSpace);// Transform light direction to tangent space.float3 lightDirectionInLocal = mul(float4(g_structDirectionalLight.directionInWorld, 0.0f), g_mWorldInv).xyz;outVS.lightDirectionInTangent  = mul(lightDirectionInLocal, toTangentSpace);positionInLocal = CartesianToSpherical(positionInLocal, 30.0f);// Transform to homogeneous clip space.outVS.posInHomogeneous = mul(float4(positionInLocal, 1.0f), g_mWorldViewProj);// Scroll texture coordinates.outVS.texcoord1 = scaledTexCoord+ g_normalOffset1;outVS.texcoord2 = scaledTexCoord+ g_normalOffset2;// Done--return the output.    return outVS;}float4 myPixelEntry(float3 toEyeInTangent    : TEXCOORD0,float3 lightDirectionInTangent : TEXCOORD1,    float2 texcoord1      : TEXCOORD2,    float2 texcoord2      : TEXCOORD3) : COLOR{// Interpolated normals can become unnormal--so normalize.// Note that toEyeW and normalW do not need to be normalized// because they are just used for a reflection and environment// map look-up and only direction matters.toEyeInTangent    = normalize(toEyeInTangent);lightDirectionInTangent = normalize(lightDirectionInTangent);// Light vector is opposite the direction of the light.float3 toLightInTangent = -lightDirectionInTangent;// Sample normal map.float3 normalInTangent1 = tex2D(g_samNormalMap1, texcoord1);float3 normalInTangent2 = tex2D(g_samNormalMap2, texcoord2);// Expand from [0, 1] compressed interval to true [-1, 1] interval.    normalInTangent1 = 2.0f * normalInTangent1 - 1.0f;    normalInTangent2 = 2.0f * normalInTangent2 - 1.0f;    // Average the two vectors.float3 normalInTangent = normalize( 0.5f * ( normalInTangent1 + normalInTangent2));// Compute the reflection vector.float3 r = reflect(lightDirectionInTangent, normalInTangent);// Determine how much (if any) specular light makes it into the eye.float s  = pow(max(dot(r, toEyeInTangent), 0.0f), g_structMaterial.specularPower);// Determine the diffuse light intensity that strikes the vertex.float d = max(dot(toLightInTangent, normalInTangent), 0.0f);// If the diffuse light intensity is low, kill the specular lighting term.// It doesn't look right to add specular light when the surface receives // little diffuse light.if(d <= 0.0f)     s = 0.0f;// Compute the ambient, diffuse and specular terms separatly. float3 spec = s * ( g_structMaterial.specular * g_structDirectionalLight.specular).rgb;float3 diffuse = d * (g_structMaterial.diffuse*g_structDirectionalLight.diffuse.rgb);float3 ambient = g_structMaterial.ambient * g_structDirectionalLight.ambient;float3 final = ambient + diffuse + spec;// Output the color and the alpha.    return float4(final, g_structMaterial.diffuse.a);}technique myTech{    pass P0    {//FillMode = WIREFRAME;        // Specify the vertex and pixel shader associated with this pass.        vertexShader = compile vs_3_0 myVertexEntry();        pixelShader  = compile ps_3_0 myPixelEntry();            }    }



好了,收工,睡觉去了,这几天要写个柏林噪声出来!!!!!!!!!!!!


可执行程序以及相关源代码请点击这里下载




读书人网 >其他相关

热点推荐