С помощью наложения нормалей (Bump Mapping) мы можем изменить нормали 2д поверхности, сделав ее объемной. Суть техники в том, что мы используем 2 текстуры. Одна хранит данные самой текстуры, а другая карту "нормалей", обычно выглядящую как первое, только в синих оттенках. Как вы видите результат очень реалистичен и, что главное, тратит мало ресурсов компьютера.
Для создания карт нормалей вы можете использовать http://developer.nvidia.com/content/nvidia-texture-tools-adobe-photoshop - там есть плагин меняющий 2д фото в карту нормалей!)
Очень хороший урок на русском - http://www.render.ru/books/show_book.php?book_id=765
Уравнение
normal,tangent и binormal - три нормали поверхности.
В результате мы можем узнать конечную нормаль:
bumpNormal = normal + bumpMap.x * tangent + bumpMap.y * binormal;
Шейдеры
Bumpmap.vs
//////////////////////////////////////////////////////////////////////////////// // Filename: bumpmap.vs //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// cbuffer MatrixBuffer { matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix; }; ////////////// // TYPEDEFS // ////////////// struct VertexInputType { float4 position : POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; }; struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; }; //////////////////////////////////////////////////////////////////////////////// // Vertex Shader //////////////////////////////////////////////////////////////////////////////// PixelInputType BumpMapVertexShader(VertexInputType input) { PixelInputType output; input.position.w = 1.0f; output.position = mul(input.position, worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix); output.tex = input.tex; output.normal = mul(input.normal, (float3x3)worldMatrix); output.normal = normalize(output.normal); //tangent output.tangent = mul(input.tangent, (float3x3)worldMatrix); output.tangent = normalize(output.tangent); // binormal output.binormal = mul(input.binormal, (float3x3)worldMatrix); output.binormal = normalize(output.binormal); return output; }
Bumpmap.ps
//////////////////////////////////////////////////////////////////////////////// // Filename: bumpmap.ps //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// Texture2D shaderTextures[2];//две текстуры SamplerState SampleType; cbuffer LightBuffer { float4 diffuseColor; float3 lightDirection; }; ////////////// // TYPEDEFS // ////////////// struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; }; //////////////////////////////////////////////////////////////////////////////// // Pixel Shader //////////////////////////////////////////////////////////////////////////////// float4 BumpMapPixelShader(PixelInputType input) : SV_TARGET { float4 textureColor; float4 bumpMap; float3 bumpNormal; float3 lightDir; float lightIntensity; float4 color; textureColor = shaderTextures[0].Sample(SampleType, input.tex); bumpMap = shaderTextures[1].Sample(SampleType, input.tex); // нормаль от (0, +1) до (-1, +1). bumpMap = (bumpMap * 2.0f) - 1.0f; // вычисляем нормаль по карте bumpNormal = input.normal + bumpMap.x * input.tangent + bumpMap.y * input.binormal; // нормализуем bumpNormal = normalize(bumpNormal); lightDir = -lightDirection; // свет lightIntensity = saturate(dot(bumpNormal, lightDir)); // цвет освещения color = saturate(diffuseColor * lightIntensity); // свет*пиксель color = color * textureColor; return color; }