Shader实现漫反射、高光反射、纹理映射
发表于2017-06-12
Shader在项目开发中的重要性就不多说了,下面介绍的是其中用的比较多的漫反射、高光反射这类光照模型,还有纹理映射这种纹理贴图,为了方便更好的去学习shader,下面就给大家详细介绍下。




漫反射效果
漫反射几何计算公式:
Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角) ) Tip:cosθ = 光方向· 法线方向
代码如下:
Shader "LJL/02_DiffuseVert" { Properties { _Color("_Color",Color)=(1,1,1,1) } SubShader { Pass { CGPROGRAM #include "UnityCG.cginc" //_LightColor0 从Unity获取第一个直射光颜色 _WorldSpaceLightPos0 从Unity获取第一个直射光世界位置 #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag struct a2v { //将Unity模型空间下的顶点位置填充给position float4 position:POSITION ; //将Unity模型空间下的法线填充给normal float3 normal:NORMAL; }; struct v2f { float4 position:SV_POSITION; fixed3 color:COLOR; }; fixed4 _Color; v2f vert(a2v v) { v2f f; f.position = mul(UNITY_MATRIX_MVP,v.position); //获取单位法线向量,并将向量在模型坐标转化为世界位置 fixed3 normalDir=normalize(mul(v.normal,(float3x3)_World2Object)); //获取世界位置下直射光单位向量(由于是平行光,在世界坐标下和在物体坐标方向一致) fixed3 lightDir=normalize(_WorldSpaceLightPos0.xyz); //漫反射计算公式:直射光颜色*max(cos0,0) cos0是发现与直射光方向 fixed3 diffuse= _LightColor0.rgb * max(dot(normalDir,lightDir),0) * _Color; //获取环境光 fixed3 ambient= UNITY_LIGHTMODEL_AMBIENT.rgb; //加上环境光颜色 f.color=ambient + diffuse; return f; } fixed4 frag(v2f f):SV_Target { return fixed4 (f.color,1); } ENDCG } } Fallback "Diffuse" }
其效果如下图所示:

但是发现阴影部分和高亮部分过渡的不够平滑,这是如果需要的话可以在片元函数里实现漫反射效果
Shader如下:
Shader "LJL/03_DiffuseFrag" { Properties { _Color("_Color",Color)=(1,1,1,1) } SubShader { Pass { CGPROGRAM #include "UnityCG.cginc" #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag fixed4 _Color; struct a2v { float4 position:POSITION ; float3 normal:NORMAL; }; struct v2f { float4 position:SV_POSITION; float3 color:COLOR; }; v2f vert(a2v v) { v2f f; f.position=mul(UNITY_MATRIX_MVP,v.position); f.color=v.normal; return f; } float4 frag(v2f f):SV_Target { float3 normalDir=normalize(mul(f.color,(float3x3)_World2Object)); float3 lightDir=normalize(_WorldSpaceLightPos0.xyz); //计算法线 float3 diffuse = _LightColor0.rgb*max(dot(normalDir,lightDir),0)*_Color; //获取环境光 float3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb; return float4(diffuse+ambient,1); } ENDCG } } Fallback "Diffuse" }
效果就更加平滑

高光反射效果
漫反射几何计算公式:
Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和视野方向的夹角
代码如下:
Shader "LJL/04_SpecularVert" { Properties { _Color("_Color",Color)=(1,1,1,1) _Range ("_Range", Range(0.0, 10.0)) = 2.0 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" float4 _Color; float _Range; struct a2v { float4 position:POSITION ;//将模型空间下的位置坐标填充给position float3 normal:NORMAL ;//将模型空间下的顶点法线填充normal }; struct v2f { float4 position:SV_POSITION ; float3 color:COLOR; }; v2f vert(a2v v) { v2f f; f.position=mul(UNITY_MATRIX_MVP,v.position); float4 vertexPos=mul(v.position,(float4x4)_World2Object); //获取视野方向 float3 cameraDir=normalize(_WorldSpaceCameraPos.xyz - vertexPos); //获取灯光方向 //float3 lightDir=_WorldSpaceLightPos0.xyz; float3 lightDir = WorldSpaceLightDir(vertexPos); float3 normalDir=normalize(mul(v.normal,(float3x3)_World2Object)); //获取反射光位置 float3 reflectDir = normalize(reflect(-lightDir,normalDir)); float3 specular = _LightColor0.rgb * pow(max(dot(reflectDir,cameraDir),0),_Range) * _Color; float3 diffuse = _LightColor0.rgb * max(dot(lightDir,normalDir),0)*_Color; f.color=specular+UNITY_LIGHTMODEL_AMBIENT.rgb+diffuse; return f; } fixed4 frag(v2f f): SV_Target { return fixed4(f.color,1); } ENDCG } } Fallback "Diffuse" }
效果图如下:

纹理映射效果
映射原理:
在顶点函数中取得在顶点对应的纹理坐标,在片元函数中获取该纹理左边的像素颜色值并与漫反射融合,赋给SV_Target
代码如下:
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject' Shader "LJL/Texture" { Properties { _Color("_Color",Color)=(1,1,1,1) _MainTex("_MainTex",2D)="white"{} } SubShader { Pass { CGPROGRAM #include "UnityCG.cginc" #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag sampler2D _MainTex; float4 _MainTex_ST;//固定写法,用于获取纹理的缩放大小和偏移大小 fixed4 _Color; struct a2v { //从语义里获取的值都是在模型坐标 float4 position:POSITION ;//将顶点位置填充给position float3 normal:NORMAL ;//将模型的法线填充给normal float4 texcoord:TEXCOORD0 ; //将该顶点的纹理坐标位置填充给texcoord }; struct v2f { float4 position:SV_POSITION ;//屏幕位置 float3 wordNormal:COLOR0;//存储世界坐标下的法线方向 float4 wordPosition:TEXCOORD0 ; //存储在世界坐标下的顶点位置 float2 uv:TEXCOORD1 ; }; v2f vert(a2v v) { v2f f; f.position=mul(UNITY_MATRIX_MVP,v.position); f.wordPosition=mul(v.position,(float4x4)unity_WorldToObject);//转化为世界坐标 f.wordNormal=mul(v.normal,(float3x3)unity_WorldToObject);//同上 //获取纹理坐标 f.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; return f; } float4 frag(v2f f):SV_Target { //获取该顶点所对应的纹理uv像素值 fixed3 color = tex2D(_MainTex,f.uv)*_Color.rgb; //获取灯光单位方向 float3 lightDir=normalize(WorldSpaceLightDir(f.wordPosition)); //获取法线单位方向 float3 normalDir=normalize(f.wordNormal); //将像素颜色值与漫反射颜色融合 float3 diffuse=_LightColor0.rgb*color*max(dot(lightDir,normalDir),0); //加上环境光渲染 float3 tempColor=diffuse+color*UNITY_LIGHTMODEL_AMBIENT.rgb; return fixed4(tempColor,1); } ENDCG } } Fallback "Diffuse" }
最终成功映射纹理同时支持纹理的缩放和偏移,效果图如下:
