Directx11教程(42) 纹理映射(12)-简单的bump mapping

 

     有时候,我们只有一个粗糙的模型,但是我们想渲染纹理细节,比如一个砖墙,我们如何在只有一个平面的时候,渲染出砖墙凹凸的效果。

   比如只有这样的墙:

 

但是我们想要这样的效果:

怎么办呢?这时候,我们可以考虑对第一张图进行处理,生成它的法向图,存储在一张纹理中,生成法向图的主要算法是:

 

      对于一张图片,假设像素排列如上图所示,Hg,Hr,Ha分别表示这些点的RGB(或灰度)值,我们得到第一个向量(1,0,Hr-Hg),第二个向量(0,1,Ha-Hg),则法向可以通过它们的差积得到:

 对所有的像素进行处理,我们可以得到一个纹理的法向图,比如第一张墙纹理贴图,经过计算,它的法向图为:

      在物体表面,每个点都都有一个(T,N,B)的局部坐标系,分别对应切向、法向,副法向,有了纹理图后,我们可以通过公式

bumpNormal = normal + bumpMap.x * tangent + bumpMap.y * binormal; 得到最终的法向。其中bumpMap为前面计算得到的法向贴图,或者说normal map。

1、首先我们改写CubeModelClass类,设置顶点格式为:

struct VertexType

{
D3DXVECTOR3 position;
D3DXVECTOR3 normal; //法向
D3DXVECTOR3 tangent; //切向
D3DXVECTOR3 binormal; //副法向
D3DXVECTOR2 texture; //纹理坐标
D3DXVECTOR4 Kd; //材质漫反射系数
D3DXVECTOR4 Ks;  //材质的高光系数
};

增加了切向和副法向的计算,主要通过顶点的uv坐标以及顶点值求得

void CubeModelClass::CalculateTangentBinormal(TempVertexType vertex1, TempVertexType vertex2, TempVertexType vertex3,
    VectorType& tangent, VectorType& binormal)
    {
    float vector1[3], vector2[3];
    float tuVector[2], tvVector[2];
    float den;
    float length;

    // 计算2个向量.
    vector1[0] = vertex2.x - vertex1.x;
    vector1[1] = vertex2.y - vertex1.y;
    vector1[2] = vertex2.z - vertex1.z;

    vector2[0] = vertex3.x - vertex1.x;
    vector2[1] = vertex3.y - vertex1.y;
    vector2[2] = vertex3.z - vertex1.z;

    // 计算tu和tv向量.
    tuVector[0] = vertex2.tu - vertex1.tu;
    tvVector[0] = vertex2.tv - vertex1.tv;

    tuVector[1] = vertex3.tu - vertex1.tu;
    tvVector[1] = vertex3.tv - vertex1.tv;

    den = 1.0f / (tuVector[0] * tvVector[1] - tuVector[1] * tvVector[0]);

    tangent.x = (tvVector[1] * vector1[0] - tvVector[0] * vector2[0]) * den;
    tangent.y = (tvVector[1] * vector1[1] - tvVector[0] * vector2[1]) * den;
    tangent.z = (tvVector[1] * vector1[2] - tvVector[0] * vector2[2]) * den;

    binormal.x = (tuVector[0] * vector2[0] - tuVector[1] * vector1[0]) * den;
    binormal.y = (tuVector[0] * vector2[1] - tuVector[1] * vector1[1]) * den;
    binormal.z = (tuVector[0] * vector2[2] - tuVector[1] * vector1[2]) * den;

    length = sqrt((tangent.x * tangent.x) + (tangent.y * tangent.y) + (tangent.z * tangent.z));

    // 归一化
    tangent.x = tangent.x / length;
    tangent.y = tangent.y / length;
    tangent.z = tangent.z / length;

    // 计算向量的长度.
    length = sqrt((binormal.x * binormal.x) + (binormal.y * binormal.y) + (binormal.z * binormal.z));

   // 归一化向量
    binormal.x = binormal.x / length;
    binormal.y = binormal.y / length;
    binormal.z = binormal.z / length;

    return;
    }

得到面的法向、切向以及副法向后,把他们赋值给顶点。

然后我们在LightTex2ShaderClass中,改变layout,然后修改lighttex2.vs和lighttex2.ps

特别是ps中,我们改变normal的计算方式:

float3 N;
float4 textureColor;
float4 textureColor1 = shaderTexture[0].Sample(SampleType, input.tex);
float4 textureColor2 = shaderTexture[1].Sample(SampleType, input.tex);

float4 bumpMap;
float3 bumpNormal;

//从范围[0,1]转换到[-1,1],因为默认法向图我们用[0,1]的这种方式打开
bumpMap  = (textureColor2 * 2.0f) - 1.0f;
N = input.worldnormal + bumpMap.x*input.worldtangent + bumpMap.y*input.worldbinormal;
N = normalize(N);

程序执行后,我们按R键,可以得到下面的结果:

因为使用的是面法向,我们再看侧面,有着不同效果,:

完整的代码请参考:

工程文件myTutorialD3D11_37

代码下载:

http://files.cnblogs.com/mikewolf2002/d3d1127-28.zip

http://files.cnblogs.com/mikewolf2002/pictures.zip

时间: 2024-09-20 13:51:14

Directx11教程(42) 纹理映射(12)-简单的bump mapping的相关文章

Directx11教程40 纹理映射(10)

     本章尝试使用纹理行列式,或者说纹理数组,在ps中,使用2个纹理,最终的像素颜色,是光照颜色*纹理1采样颜色*纹理2采样颜色,主要是想达到如下的效果:    把这两个图像以及光照产生的颜色融合生成以下图像:   为此我们新建一个lighttex2.ps文件,在其中定义: //两个纹理,可用于纹理混合,bump mapping等等 Texture2D shaderTexture[2]; SamplerState SampleType; - float4 textureColor1 = sh

Directx11教程(5) 画一个简单的三角形(1)

      在本篇教程中,我们将通过D3D11画一个简单的三角形.在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作是由GPU固定管线完成,比如光栅化操作),最常用的shader操作是顶点shader(vertex shader)和像素shader(pixel shader).其实shader就是在GPU中执行的代码,这些代码被driver编译成硬件依赖的机器码,最终被GPU中shader pipe执行,从而完成3D渲染.D3D11中shader是用一种类C的语言HLSL编

Directx11教程(19) 画一个简单的地形

      通常我们在xz平面定义一个二维的网格,然后y的值根据一定的函数计算得到,比如正弦.余弦函数的组合等等,可以得到一个看似不错的地形或者水面的效果. 在本教程中我们修改ModelClass.h和ModelClass.cpp,得到一个近似的地形.         在本章代码中,我们定义300*300=90000个顶点,共(300-1)(300-1)*2个三角形,每个网格的大小都为1.     我们得到y值的函数为: float ModelClass::getHeight(float x,

Directx11教程(31) 纹理映射(1)

        在前面的例子中,我们要么是直接给顶点赋颜色值,要么是在顶点属性中设置Diffuse和Specular系数,从而根据光照参数计算得到物体表面颜色,但这样得到的颜色真实感要差很多.如果我们直接把一副图像映射到三角形面上,从而得到物体表面颜色值,效果会好很多,比如下面的两幅图,右边的图是把一副图片映射到2个三角形上.         甚至,我们还可以直接使用图像的颜色值做为顶点(或者pixel)的diffuse值,融合光照计算公式,得到最终的表面颜色值,这样会有更好的效果.     

Directx11教程(43) 纹理映射(13)-动态纹理映射

     本篇教程中,我们将在前面基于光照的地形与水面程序里面加上纹理映射,而且我们会基于时间动态改变水面的纹理坐标,实现水面纹理波动的效果.       地形(山谷)以及水面都是基于网格的平面.       对于地形,修改顶点类型为: struct VertexType     {     D3DXVECTOR3 position;     D3DXVECTOR3 normal;     D3DXVECTOR2 texture; //纹理坐标     D3DXVECTOR4 Kd; //材质的

Directx11教程(6) 画一个简单的三角形(2)

     在上篇教程中,我们实现了在D3D11中画一个简单的三角形,但是,当我们改变窗口大小时候,三角形形状却随着窗口高宽比例改变而改变,如下图所示:           这是因为我们改变了窗口大小,但后缓冲大小在程序初始化时候,已经被指定,不随着窗口改变而改变,这样在视口映射下,我们所渲染的三角形就改变了形状. 下面我们将对程序进行一些小的改动,从而实现改变窗口大小,而渲染的图形形状不变.     首先是SystemClass.cpp,主要是增加了WM_SIZE消息的处理,当窗口大小改变时候,

Directx11教程(35) 纹理映射(5)

    到现在为止,我们的TextureClass初始化函数非常简单,说白了就是一行代码: result = D3DX11CreateShaderResourceViewFromFile(device, filename, NULL, NULL, &m_texture, NULL);       这行代码装入一个dds文件,而且其它选项都设置为NULL,这时系统会使用装入文件本身的格式,比如我们装入的tong.dds, 前面我们为其产生了mipmaps层,并且设置surface格式为DXT5(对

Directx11教程37 纹理映射(7)

    本章是在教程35.36的基础上来实现一个光照纹理结合的程序,就是把场景中旋转的cube加上纹理.    lighttex.vs中顶点的结构现在为: struct VertexInputType {     float4 position : POSITION;     float3 normal : NORMAL;     float2 tex : TEXCOORD0; //纹理坐标     float4 Kd : DIFFUSE;     float4 Ks: SPECULAR; };

Directx11教程(34) 纹理映射(4)

    本篇教程中,我们尝试在myTutorialD3D_27中改变采样状态描述符的各种设置,看纹理贴图的方式有什么变化. 原始的代码是:     // 创建纹理采样描述符 samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampler