




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
3D游戏引擎系列七Jim Blinn在1978发表了一篇名为:“Simulationof Wrinkled Surfaces”,提出了Bump Mapping这个东东。BumpMapping通过一张Height Map记录各象素点的高度信息,有了高度信息,就可以计算HeightMap中当前象素与周围象素的高度差,这个高度差就代表了各象素的坡度,用这个坡度信息去绕动法向量,得到最终法向量,用于光照计算。在前面的博客中也有介绍过高光法线时介绍过TBN,在这里利用TBN渲染Bumpmapping实现原理。 GPU渲染属于可编程流水线,在使用Shader编程时首先要明白其实现原理,我们可以通过可编程流水线了解一下其原理:上图中显示了TBNMatrix,T表示的是切向量,B表示次法线向量,N表示的是法线向量,这个也是求Bump Mapping必须要计算得到的,TBN Matrix可以将其放到GPU中计算得到的。下面我们就一步步给读者介绍其实现,只用文字很难描述清楚,还是通过图看的比较明白。为了帮助读者理解Tangent 空间向量,先给读者看一下场景中的四边形,纹理图片是要贴到四边形上的,如下图所示的效果:图中显示的是纹理坐标的关系,左下角是(0,0),右上角是(1,1)。现在,我们需要找到两个顶点的切线。“S切线”点的方向的纹理坐标,“T切线”点的方向的纹理坐标。这两个切线和法线是一个顶点的“基础”。它们定义了一个坐标空间-“切向量空间”。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; VECTOR3D 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 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上查看代码片派生到我的代码片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).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(); 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 allocate 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; usPrecision+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).GetRotatedZ(i*360.0f/torusPrecision); verticesi.normal=verticesi.tTangent. CrossProduct(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; 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=verticesi.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*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中运行得到的,它渲染的凹凸效果如下所示: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_LightPositionViewSpace; 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_fReflectivity = 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 = 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_OneOverSqrLightRadius); 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.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 = 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 texRefCoord : 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) : 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 : 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 = CalReflect(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 halfDir = 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_EnvMapSampler, 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 float3x3 tangent = CalTangentFrame(inData.normal); lightVec = mul(tangent, lightVec); eyeVec = mul(tangent, eyeVec); half3 lightDir = normalize(lightVec); half3 eyeDir = normalize(eyeVec); half3 halfDir = 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 * 0.5f; float4 envColor = g_fReflectivity * texCUBE(g_EnvMapSampler, inData.texRefCoord); float4 Ia = envColor * g_MaterialAmbient; return (Ia + Id + Is) * atten; float4 PS_ParallaxMapping(VS_OUTPUT inData) : COLOR0 half3 lightVec = g_LightPositionViewSpace - inData.worldViewPos; half3 eyeVec = -inData.worldViewPos; / get the normal from normal map half height = tex2D(g_HeightMapSampler, inData.texCoord); half2 newTexCoord = inData.texCoord + (height * g_fScale - g_fBias) * normalize(eyeVec).xy; half3 nor
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 45982.2-2025第二代高温超导体微连接第2部分:焊接与试验人员资格
- 森林灭火知识培训内容课件
- 初级电焊工理论考试题及答案
- 2025《机械员》考试题库附答案(综合题)
- 2025年草原生态修复师高级面试题集锦
- 2025年注册验船师资格考试(A级船舶检验专业案例分析)综合练习题及答案二
- 2025公务员(国考)考试题库(及答案)
- 解数第293期:2025年618第一波回顾:从618数据看懂消费趋势
- 2025年殡仪馆工作人员职责与工作流程面试题
- 2026届江苏省泰州市兴化市第一中学化学高一第一学期期末监测试题含解析
- 危险化学品(储存、生产、使用)企业安全风险辨识分级管控清单
- 幼儿园劳务分包合同范本
- 第二语言习得研究要略
- 核工程与核技术毕业设计(论文)闪烁探测器探测效率与γ射线能量关系的数值模拟
- 期货交易基础知识测试题库
- 混凝土搅拌站安全预评价报告
- 高一第一堂班会课
- 【学生生涯规划系列】高一上学期生涯规划讲座课件
- 焊接工艺要求
- JJF(电子) 31502-2010 静电腕带/脚盘测试仪校准规范-(高清现行)
- 国学武术操太极拳表演活动流程
评论
0/150
提交评论