Shader入门精要学习记录2
2018-02-06 18:05
399 查看
接上篇准备实现法线纹理的贴图;
首先个人理解的是物体的凹凸感受法线方向影响, diffuse光照模型可以理解。凹凸贴图由法线贴图和高度贴图。
纹理的另一种常见应用就是凹凸映射。凹凸映射不会真的改变模型的顶点位置,只是让模型看起来好像“凹凸不平”,但可以从模型的轮廓处看出“破绽”。
有两种方法进行凹凸映射:一种方法是使用一张高度纹理(heightmap)来模拟表面位移,然后得到一个修改后的法线值;另一种是使用一张法线纹理来直接储存表面法线,这种方法又被称为法线映射(normal mapping)。
高度纹理
使用一张高度图来实现凹凸映射。高度图中储存的是强度值,它用于表示模型局部的海拔高度。因此,颜色越浅表明该位置的表面越向外凸起,而颜色越深表明该位置越向里凹。这种方法的好处就是非常直观,但缺点是计算更加复杂,在实时计算时不能直接得到表面法线,而需要由像素的灰度值计算而得,因此需要消耗更多的性能。高度图通常会和法线映射一起使用,用于给出表面凹凸的额外信息。也就是说,我们通常会使用法线映射来修改光照。
法线纹理
法线纹理储存的就是表面的法线方向,由于法线方向的分量范围在[-1,1],而像素的分量范围为[0,1],因此我们需要做一个映射,通常的映射就是: pixel=(normal+1)/2
其它博客对于向量从世界空间到切线空间和切线空间到世界空间的讲解,还不错~http://blog.csdn.net/liu_if_else/article/details/73604356
在片元着色器中通过纹理采样得到切线空间下的法线,然后再与切线空间下的视角方向、光照方向等进行计算,得到最终的光照结果。为此,我们首先需要在顶点着色器中把视角方向和光照方向从模型空间变换到切线空间中, 即我们需要知道模型空间到切线空间的变换矩阵。这个变换矩阵的逆矩阵,即从切线空间到模型空间的变换矩阵是非常容易得到的,我们在顶点着色器中按 切线(X轴),副切线( Y轴 ),法线(Z轴)的顺数排列即可得到。 如果一个变换中仅存在平移和旋转变换,那么这个变换的逆矩阵就等于它的转置矩阵,而从切线空间到模型空间的变换正是符合这样要求的变换。因此,在模型空间到切线空间的变换矩阵就是从切线空间到模型空间的变换矩阵的转置矩阵,我们把切线(X轴),副切线(Y轴)、法线(z轴)的顺序按行排列即可得到。
切线空间:
在3D世界中定了如此多的坐标系,每个坐标系当然都有它的用途。比如局部空间,或者叫模型空间,它的目的就是方便我们对3D模型进行建模。在这个空间中,我们不需要考虑该模型在场景中可能出现的位置、朝向等众多细节,而专注于模型本身。在世界空间中,我们关心的问题是场景中各个物体的位置、朝向,即如何构建场景,而不必关注摄像机的观察位置及其朝向。可见,一个坐标系的根本用途,即让我们在处理不同的问题时,能够以合适的参照系,抛开不相关的因素,从而减小问题的复杂度。
知乎上切线空间
http://windsmoon.com/2017/11/28/%E5%88%87%E7%BA%BF%E7%A9%BA%E9%97%B4-Tangent-Space-%E7%9A%84%E8%AE%A1%E7%AE%97%E4%B8%8E%E5%BA%94%E7%94%A8/
切线空间 (Tangent Space) 与 世界空间 (World Space) 和 观察空间 (View Space) 一样,都是一个坐标空间,它是由顶点所构成的平面的 UV 坐标轴以及表面的法线所构成,一般用 T (Tangent), B (Bitangent), N (Normal) 三个字母表示,即切线,副切线,法线, T 对应 UV 中的 U, B 对应 UV 中的 V。
这里可能会有一个疑问,就是为什么 T 对应 UV 中的 U, B 对应 UV 中的 V 。理论上,只要 T 和 B 垂直且都位于三角形的平面内,就可以达到使用切线空间的目的,因为这样我们总可以把所有需要的数据变换到同一个坐标空间下,但由于我们知道 UV 坐标的值,所以用 UV 坐标来对应 T 和 B 计算出数据了
额感觉书上也不是很详细:
先介绍新出现的函数:
ObjSpaceViewDir:
ObjSpaceLightDir:
UnPackNormal:
TAGENT_SPACE_ROTATION
额 再记录次向量点乘和叉乘
在片元着色器中把法线方向从切线空间变换到世界空间下
思路:
在顶点着色器中计算从切线空间到世界空间的变换矩阵,并把他传递给片元着色器。变换矩阵可有顶点的切线、副切线和法线在世界空间下的表示得到。
首先个人理解的是物体的凹凸感受法线方向影响, diffuse光照模型可以理解。凹凸贴图由法线贴图和高度贴图。
纹理的另一种常见应用就是凹凸映射。凹凸映射不会真的改变模型的顶点位置,只是让模型看起来好像“凹凸不平”,但可以从模型的轮廓处看出“破绽”。
有两种方法进行凹凸映射:一种方法是使用一张高度纹理(heightmap)来模拟表面位移,然后得到一个修改后的法线值;另一种是使用一张法线纹理来直接储存表面法线,这种方法又被称为法线映射(normal mapping)。
高度纹理
使用一张高度图来实现凹凸映射。高度图中储存的是强度值,它用于表示模型局部的海拔高度。因此,颜色越浅表明该位置的表面越向外凸起,而颜色越深表明该位置越向里凹。这种方法的好处就是非常直观,但缺点是计算更加复杂,在实时计算时不能直接得到表面法线,而需要由像素的灰度值计算而得,因此需要消耗更多的性能。高度图通常会和法线映射一起使用,用于给出表面凹凸的额外信息。也就是说,我们通常会使用法线映射来修改光照。
法线纹理
法线纹理储存的就是表面的法线方向,由于法线方向的分量范围在[-1,1],而像素的分量范围为[0,1],因此我们需要做一个映射,通常的映射就是: pixel=(normal+1)/2
其它博客对于向量从世界空间到切线空间和切线空间到世界空间的讲解,还不错~http://blog.csdn.net/liu_if_else/article/details/73604356
在切线空间下的计算
基本思路:在片元着色器中通过纹理采样得到切线空间下的法线,然后再与切线空间下的视角方向、光照方向等进行计算,得到最终的光照结果。为此,我们首先需要在顶点着色器中把视角方向和光照方向从模型空间变换到切线空间中, 即我们需要知道模型空间到切线空间的变换矩阵。这个变换矩阵的逆矩阵,即从切线空间到模型空间的变换矩阵是非常容易得到的,我们在顶点着色器中按 切线(X轴),副切线( Y轴 ),法线(Z轴)的顺数排列即可得到。 如果一个变换中仅存在平移和旋转变换,那么这个变换的逆矩阵就等于它的转置矩阵,而从切线空间到模型空间的变换正是符合这样要求的变换。因此,在模型空间到切线空间的变换矩阵就是从切线空间到模型空间的变换矩阵的转置矩阵,我们把切线(X轴),副切线(Y轴)、法线(z轴)的顺序按行排列即可得到。
切线空间:
在3D世界中定了如此多的坐标系,每个坐标系当然都有它的用途。比如局部空间,或者叫模型空间,它的目的就是方便我们对3D模型进行建模。在这个空间中,我们不需要考虑该模型在场景中可能出现的位置、朝向等众多细节,而专注于模型本身。在世界空间中,我们关心的问题是场景中各个物体的位置、朝向,即如何构建场景,而不必关注摄像机的观察位置及其朝向。可见,一个坐标系的根本用途,即让我们在处理不同的问题时,能够以合适的参照系,抛开不相关的因素,从而减小问题的复杂度。
知乎上切线空间
http://windsmoon.com/2017/11/28/%E5%88%87%E7%BA%BF%E7%A9%BA%E9%97%B4-Tangent-Space-%E7%9A%84%E8%AE%A1%E7%AE%97%E4%B8%8E%E5%BA%94%E7%94%A8/
切线空间 (Tangent Space) 与 世界空间 (World Space) 和 观察空间 (View Space) 一样,都是一个坐标空间,它是由顶点所构成的平面的 UV 坐标轴以及表面的法线所构成,一般用 T (Tangent), B (Bitangent), N (Normal) 三个字母表示,即切线,副切线,法线, T 对应 UV 中的 U, B 对应 UV 中的 V。
这里可能会有一个疑问,就是为什么 T 对应 UV 中的 U, B 对应 UV 中的 V 。理论上,只要 T 和 B 垂直且都位于三角形的平面内,就可以达到使用切线空间的目的,因为这样我们总可以把所有需要的数据变换到同一个坐标空间下,但由于我们知道 UV 坐标的值,所以用 UV 坐标来对应 T 和 B 计算出数据了
shader实现切线空间
/ Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Practice/NormalMap" { Properties { _MainTex("MainTex",2D)="white"{} _BumpTex("NormalMap",2D)="white"{} _MainColor("Main Color",COLOR)=(1,1,1,1) _Diffuse("DiffuseColor",COLOR)=(1,1,1,1) _Specular("SpecuColor",COLOR)=(1,1,1,1) _Gloss("Gloss",Range(8,20))=8 _BumpScale("BumpScale",float)=1.0 } SubShader { Pass { Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" fixed4 _MainColor; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpTex; float4 _BumpTex_ST; float _BumpScale; struct a2v{ float4 pos:POSITION; float3 normal:NORMAL; float4 tangent:TANGENT; //获取切线 float4 texcoord:TEXCOORD; }; struct v2f{ float4 vertex:SV_POSITION; float4 texcoord:TEXCOORD; float3 lightDir:TEXCOORD1; float3 viewDir:TEXCOORD2; }; v2f vert(a2v v) { v2f o; o.vertex=UnityObjectToClipPos(v.pos); o.texcoord.xy=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; o.texcoord.zw=v.texcoord.xy*_BumpTex_ST.xy+_BumpTex_ST.zw; float3 binormal=cross(normalize(v.normal),normalize(v.tangent.xyz))*v.tangent.w; float3x3 rotation= float3x3(v.tangent.xyz,binormal,v.normal); d0ec o.lightDir=mul(rotation,normalize(ObjSpaceLightDir(v.pos))); o.viewDir=mul(rotation,normalize(ObjSpaceViewDir(v.pos))); return o; } fixed4 frag(v2f v):SV_TARGET { fixed4 albedo=tex2D(_MainTex,v.texcoord.xy)*_MainColor; fixed3 tangentLightDir=normalize(v.lightDir); fixed3 tangentViewDir=normalize(v.viewDir); fixed4 packedNormal=tex2D(_BumpTex,v.texcoord.zw); //法线 fixed3 TNormal; TNormal.xy = (packedNormal.xy*2-1)*_BumpScale; TNormal.z = sqrt(1.0-saturate(dot(TNormal.xy,TNormal.xy))); // tangentNormal = UnpackNormal(packedNormal); //tangentNormal.xy *= _BumpScale; //tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy))); fixed4 ambient=UNITY_LIGHTMODEL_AMBIENT*albedo; fixed4 diffuse=_Diffuse*albedo*_LightColor0*saturate(dot(TNormal,tangentLightDir)); fixed3 hDir=normalize(tangentViewDir+tangentLightDir); fixed4 specular=_Specular*_LightColor0*pow(saturate(dot(TNormal,hDir)),_Gloss); return diffuse+ambient+specular; } ENDCG } } }
额感觉书上也不是很详细:
先介绍新出现的函数:
ObjSpaceViewDir:
ObjSpaceLightDir:
UnPackNormal:
TAGENT_SPACE_ROTATION
额 再记录次向量点乘和叉乘
接下来实现在世界空间下的计算
在世界空间下的计算的思路是把储存法线贴图的信息由切线空间变换到世界空间,在片元着色器中把法线方向从切线空间变换到世界空间下
思路:
在顶点着色器中计算从切线空间到世界空间的变换矩阵,并把他传递给片元着色器。变换矩阵可有顶点的切线、副切线和法线在世界空间下的表示得到。
Shader "Unity Shaders Book/Chapter 7/Normal Map In World Space" { Properties { _Color ("Color Tint", Color) = (1, 1, 1, 1) _MainTex ("Main Tex", 2D) = "white" {} _BumpMap ("Normal Map", 2D) = "bump" {} _BumpScale ("Bump Scale", Float) = 1.0 _Specular ("Specular", Color) = (1, 1, 1, 1) _Gloss ("Gloss", Range(8.0, 256)) = 20 } SubShader { Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpMap; float4 _BumpMap_ST; float _BumpScale; fixed4 _Specular; float _Gloss; struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float4 uv : TEXCOORD0; float4 TtoW0 : TEXCOORD1; float4 TtoW1 : TEXCOORD2; float4 TtoW2 : TEXCOORD3; }; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw; float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // Compute the matrix that transform directions from tangent space to world space // Put the world position in w component for optimization o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x); o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y); o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); return o; } fixed4 frag(v2f i) : SV_Target { // Get the position in world space float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w); // Compute the light and view dir in world space fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos)); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos)); // Get the normal in tangent space fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw)); bump.xy *= _BumpScale; bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy))); // Transform the narmal from tangent space to world space bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump))); fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir)); fixed3 halfDir = normalize(lightDir + viewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(bump, halfDir)), _Gloss); return fixed4(ambient + diffuse + specular, 1.0); } ENDCG } } FallBack "Specular" }
相关文章推荐
- Shader入门精要学习记录4
- Shader入门精要学习记录5
- Shader入门精要学习记录六
- Shader入门精要学习记录七
- UnityShader入门精要学习笔记(十九):卷积与边缘检测
- 学习UnityShader入门精要笔记1——渲染流程概述
- Unity Shader入门精要学习笔记 - 第16章 Unity中的渲染优化技术
- UnityShader入门精要学习笔记(十二):渲染路径与光源类型
- UnityShader入门精要学习笔记(二十一):深度和法线纹理
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
- UnityShader入门精要学习笔记(九):基础纹理之渐变纹理与遮罩纹理
- UnityShader入门精要学习笔记(十一):透明效果-下部分
- UnityShader入门精要学习笔记(十三):光照衰减与Unity阴影
- Unity Shader入门精要学习笔记 - 第11章 让画面动起来
- Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
- UnityShader入门精要学习笔记(十):透明效果-上部分
- UnityShader入门精要学习笔记(二十):运动模糊
- 凹凸映射 Bump mapping(unityshader入门精要学习笔记)
- Unity3D - Shader入门精要学习笔记