1、3D游戏引擎系列七3D游戏引擎系列七Jim Blinn在1978发表了一篇名为:“Simulationof Wrinkled Surfaces”,提出了Bump Mapping这个东东。BumpMapping通过一张Height Map记录各象素点的高度信息,有了高度信息,就可以计算HeightMap中当前象素与周围象素的高度差,这个高度差就代表了各象素的坡度,用这个坡度信息去绕动法向量,得到最终法向量,用于光照计算。在前面的博客中也有介绍过高光法线时介绍过TBN,在这里利用TBN渲染Bumpmapping实现原理。 GPU渲染属于可编程流水线,在使用Shader编程时首先要明白其实现原理,我
2、们可以通过可编程流水线了解一下其原理:上图中显示了TBNMatrix,T表示的是切向量,B表示次法线向量,N表示的是法线向量,这个也是求Bump Mapping必须要计算得到的,TBN Matrix可以将其放到GPU中计算得到的。下面我们就一步步给读者介绍其实现,只用文字很难描述清楚,还是通过图看的比较明白。为了帮助读者理解Tangent 空间向量,先给读者看一下场景中的四边形,纹理图片是要贴到四边形上的,如下图所示的效果:图中显示的是纹理坐标的关系,左下角是(0,0),右上角是(1,1)。现在,我们需要找到两个顶点的切线。“S切线”点的方向的纹理坐标,“T切线”点的方向的纹理坐标。这两个切线
3、和法线是一个顶点的“基础”。它们定义了一个坐标空间-“切向量空间”。s 切线,t切线和法线分别是用x,y,z轴表示的,TBN矩阵是从物体空间转换到切线空间,矩阵的表示如下所示:( Sx Sy Sz )( Tx Ty Tz )( Nx Ny Nz )S也是切线空间中的一种,我们也可以将其表示为Binormal,现在的主要问题是求解S,T,N向量。先抛开GPU编程,先用C+代码实现一遍,首先定义一个类用于存储TBN,类的存储代码如下所示:cpp view plain copyclass TORUS_VERTEX public: VECTOR3D position; float s, t; VECT
4、OR3D sTangent, tTangent; VECTOR3D normal; VECTOR3D tangentSpaceLight; ; 结构体定义好了后,接下来,我们假设在场景中放置一个圆环,这个圆环有48个点组成,用于存储顶点和索引链表, 代码类完整定义如下所示:cpp view plain copy#ifndef TORUS_H #define TORUS_H class TORUS_VERTEX public: VECTOR3D position; float s, t; VECTOR3D sTangent, tTangent; VECTOR3D normal; VECTOR3D
5、 tangentSpaceLight; ; class TORUS public: TORUS(); TORUS(); bool InitTorus(); int numVertices; int numIndices; unsigned int * indices; TORUS_VERTEX * vertices; ; const int torusPrecision=48; #endif 现在开始计算圆环的顶点位置,法线,切向量空间,首先我们把圆环在XY平面上的示例图展示如下:接下来在圆环上的材质实现法线和切向量空间的代码如下所示:cpp view plain copy 在CODE上查看代
6、码片派生到我的代码片for(int i=0; itorusPrecision+1; i+) verticesi.position=VECTOR3D(1.5f, 0.0f, 0.0f).GetRotatedZ(i*360.0f/torusPrecision)+VECTOR3D(4.0f, 0.0f, 0.0f); verticesi.s=0.0f; verticesi.t=(float)i/torusPrecision; verticesi.sTangent.Set(0.0f, 0.0f, -1.0f); verticesi.tTangent=VECTOR3D(0.0f, -1.0f, 0.0f
7、).GetRotatedZ(i*360.0f/torusPrecision); verticesi.normal=verticesi.tTangent.CrossProduct(verticesi.sTangent); 图中实现的是静止不动的圆环,利用for循环实现了TBN向量,转动后的效果计算也是类似的,下面把完整的代码给读者展现一下:cpp view plain copy 在CODE上查看代码片派生到我的代码片#include #include #include #include Maths/Maths.h #include TORUS.h TORUS:TORUS() InitTorus(
8、); TORUS:TORUS() if(indices) delete indices; indices=NULL; if(vertices) delete vertices; vertices=NULL; bool TORUS:InitTorus() numVertices=(torusPrecision+1)*(torusPrecision+1); numIndices=2*torusPrecision*torusPrecision*3; vertices=new TORUS_VERTEXnumVertices; if(!vertices) printf(Unable to allocat
9、e memory for torus verticesn); return false; indices=new unsigned intnumIndices; if(!indices) printf(Unable to allocate memory for torus indicesn); return false; /calculate the first ring - inner radius 4, outer radius 1.5 for(int i=0; iusPrecision+1; i+) verticesi.position=VECTOR3D(1.5f, 0.0f, 0.0f
10、).GetRotatedZ(i*360.0f/torusPrecision)+ VECTOR3D(4.0f, 0.0f, 0.0f); verticesi.s=0.0f; verticesi.t=(float)i/torusPrecision; verticesi.sTangent.Set(0.0f, 0.0f, -1.0f); verticesi.tTangent=VECTOR3D(0.0f, -1.0f, 0.0f).GetRotatedZ(i*360.0f/torusPrecision); verticesi.normal=verticesi.tTangent. CrossProduct
11、(verticesi.sTangent); /rotate this to get other rings for(int ring=1; ringtorusPrecision+1; ring+) for(int i=0; itorusPrecision+1; i+) verticesring*(torusPrecision+1)+i.position=verticesi.position.GetRotatedY(ring*360.0f/torusPrecision); verticesring*(torusPrecision+1)+i.s=2.0f*ring/torusPrecision;
12、verticesring*(torusPrecision+1)+i.t=verticesi.t; verticesring*(torusPrecision+1)+i.sTangent=verticesi.sTangent.GetRotatedY(ring*360.0f/torusPrecision); verticesring*(torusPrecision+1)+i.tTangent=verticesi.tTangent.GetRotatedY(ring*360.0f/torusPrecision); verticesring*(torusPrecision+1)+i.normal=vert
13、icesi.normal.GetRotatedY(ring*360.0f/torusPrecision); /calculate the indices for(int ring=0; ringtorusPrecision; ring+) for(int i=0; itorusPrecision; i+) indices(ring*torusPrecision+i)*2)*3+0=ring*(torusPrecision+1)+i; indices(ring*torusPrecision+i)*2)*3+1=(ring+1)*(torusPrecision+1)+i; indices(ring
14、*torusPrecision+i)*2)*3+2=ring*(torusPrecision+1)+i+1; indices(ring*torusPrecision+i)*2+1)*3+0=ring*(torusPrecision+1)+i+1; indices(ring*torusPrecision+i)*2+1)*3+1=(ring+1)*(torusPrecision+1)+i; indices(ring*torusPrecision+i)*2+1)*3+2=(ring+1)*(torusPrecision+1)+i+1; return true; 以上代码实现的圆环效果是在CPU中运行
15、得到的,它渲染的凹凸效果如下所示:CPU上的效率没有GPU运行效率高,还是建议读者在GPU中实现出来,最后把在GPU中实现的Shader代码给读者展示一下:cpp view plain copy 在CODE上查看代码片派生到我的代码片/ 矩阵 float4x4 g_matWorld : World; float4x4 g_matWorldView : WorldView; float4x4 g_matWorldViewProj : WorldViewProj; float4x4 g_matInverWorldView : InverseWorldView; / 灯光 float4 g_Ligh
16、tPositionViewSpace; float g_OneOverSqrLightRadius; float4 g_EyePosition; / 材质 float g_MaterialAmbient; float g_MaterialDiffuse; float g_MaterialSpecular; float g_MaterialEmissive; float g_MaterialShininess; / 纹理图片 texture g_texEnvMap; texture g_texNormalMap; texture g_texHeightMap; float g_fReflecti
17、vity = 0.5f; float g_fScale = 0.04f; / 0, 0.05 static float g_fBias = g_fScale * 0.5f; / - / Sampler / - sampler g_EnvMapSampler = sampler_state Texture = ; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; ; sampler g_NormalMapSampler = sampler_state Texture = (g_texNormalMap); MinFilter
18、= Linear; MagFilter = Linear; MipFilter = Linear; ; sampler g_HeightMapSampler = sampler_state Texture = (g_texHeightMap); MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; ; / - / Utilities / - half CalAttenuation(half3 lightVec) return saturate(1 - dot(lightVec, lightVec) * g_OneOverSqrL
19、ightRadius); float3x3 CalInvertMatrix3X3(float3x3 M) float det = dot(cross(M0, M1), M2); float3x3 T = transpose(M); return float3x3( cross(T1, T2), cross(T2, T0), cross(T0, T1) / det; /计算TBN矩阵 float3x3 CalTangentFrame(float3 N) float3 binormal; float3 tangent; float3 c1 = cross(N, float3(0.0, 0.0, 1
20、.0); float3 c2 = cross(N, float3(0.0, 1.0, 0.0); if(length(c1)length(c2) tangent = c1; else tangent = c2; tangent = normalize(tangent); binormal = cross(N, tangent); binormal = normalize(binormal); return float3x3(tangent, binormal, N); float3 CalReflect(float4 position, float3 normal) float3 Normal
21、 = mul(normalize(normal), g_matWorld); float3 PosWorld = mul(ition, g_matWorld); float3 ViewDir = normalize(PosWorld - g_EyePosition.xyz); /return refract(ViewDir, Normal, .99); return reflect(ViewDir, Normal); / - / vertex & pixel shader / - struct VS_OUTPUT_ENV float4 position : POSITION; float3 t
22、exRefCoord : TEXCOORD0; ; VS_OUTPUT_ENV VS_EnvMapping(float4 position : POSITION, float3 normal : NORMAL) VS_OUTPUT_ENV OUT = (VS_OUTPUT_ENV)0; OUT.position = mul(position, g_matWorldViewProj); OUT.texRefCoord = CalReflect(position, normal); return OUT; float4 PS_EnvMapping(float3 tex : TEXCOORD0) :
23、 COLOR0 return g_fReflectivity * texCUBE(g_EnvMapSampler, tex); / - struct VS_OUTPUT float4 position : POSITION; float3 normal : TEXCOORD0; float2 texCoord : TEXCOORD1; float3 worldViewPos : TEXCOORD2; float3 texRefCoord : TEXCOORD3; ; VS_OUTPUT VS_NormalMapping(float4 pos : POSITION, float3 normal
24、: NORMAL, float2 texCoord : TEXCOORD0) VS_OUTPUT outData = (VS_OUTPUT)0; outData.position = mul(pos, g_matWorldViewProj); outData.normal = normalize(mul(normal, (float3x3)g_matWorldView); outData.texCoord = texCoord; outData.worldViewPos = mul(pos, g_matWorldView).xyz; outData.texRefCoord = CalRefle
25、ct(pos, normal); return outData; float4 PS_Phong(VS_OUTPUT inData) : COLOR0 half3 lightVec = g_LightPositionViewSpace - inData.worldViewPos; half3 eyeVec = -inData.worldViewPos; half3 normal = normalize(inData.normal); half3 lightDir = normalize(lightVec); half3 eyeDir = normalize(eyeVec); half3 hal
26、fDir = normalize(eyeDir + lightDir); half3 light = lit(dot(lightDir, normal), dot(halfDir, normal), g_MaterialShininess); half atten = CalAttenuation(lightVec); float4 Id = light.y * g_MaterialDiffuse; float4 Is = light.z * g_MaterialSpecular; float4 envColor = g_fReflectivity * texCUBE(g_EnvMapSamp
27、ler, inData.texRefCoord); float4 Ia = envColor * g_MaterialAmbient; return (Ia + Id + Is) * atten * envColor; float4 PS_NormalMapping(VS_OUTPUT inData) : COLOR0 half3 lightVec = g_LightPositionViewSpace - inData.worldViewPos; half3 eyeVec = -inData.worldViewPos; / get the normal m normal map half3 normal = normalize(tex2D(g_NormalMapSampler, inData.texCoord).xyz * 2.0 - 1.0); / transform the vector from world space to tangent space float
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1