//Unity Shader Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _SelfColor("自发光", Color) = (0.7,0.7,0.7,1) _guangzedu("光泽度", Range(0.01,100) ) = 70 _Detail ("细节纹理", 2D) = "gray" {} } //C# Render.material.SetColor("_SelfColor", new Color(0.549f, 0.549f, 0.549f));
获取到相应使用这个Shader的材质,然后调用Set...方法设置相应的属性,名字对应上就OK了。
顶点着色器是GPU的第一个阶段,主要完成坐标变换和逐顶点关照。顶点坐标由模型空间变换到齐次剪裁空间。
裁剪是在剪裁空间下去掉那些摄像机视野看不到图元。一个图元和摄像机视野的关系有三种:完全在视野内、部分在视野内、完全在视野外。(不可编程的)
屏幕映射的任务是把每个图元的x和y坐标转换到屏幕坐标系,z坐标这一步不处理。
三角形设置就是计算得到三角形边界的表达方式,三角形遍历检查在三角形网格覆盖的像素生成一个片元。
片元着色器就是可以控制那些片元的颜色的输出。为了在片元着色器中进行纹理采样,我们通常会在顶点着色器阶段输出每个顶点对应的纹理坐标,然后经过光栅化阶段对三角网格的3个顶点对应的纹理坐标进行插值后,就可以得到其覆盖的片元的纹理坐标了。
逐片元操作其实就是合并,就是检查片元是否可见(测试),然后是覆盖到还是混合到颜色缓冲区去。
渲染流程分为:应用阶段、几何阶段、光栅化阶段。
应用阶段是CPU上运行的,准备场景的数据(光源、相机、模型等),粗粒剔除不可见的物体,设置渲染状态(材质、Shader等)。这一阶段最重要的输出就是渲染所需要的几何信息-即渲染图元,传递给下一个阶段(几何阶段)。
几何阶段是GPU上运行的,主要是逐顶点的把它们变换到屏幕空间去,再交给光栅器处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕的二维顶点坐标、每个顶点对应的深度值、着色等信息,传递给下一个阶段。
光栅化阶段是GPU上运行的,主要会根据上一阶段传过来的屏幕顶点信息进行插值处理(纹理和颜色),然后再进行逐像素处理。
法线贴图需要记录的是一个点的方向,而对应的图片的记录颜色RGBA,完全可以够用来记录法线方向。已知到RGB的取值范围是(0-255),而记录法线方向的范围是(-1,1)。所以需要做一个映射,假设RGB取值除以255那么范围为(0,1),此时做一个(0,1)到(-1,1)的映射即可。很简单的乘2-1法,和半兰伯特技巧般的。这也是为什么法线贴图老是蓝蓝的,因为一般来说正常的一致的法线是(0,0,1)(不凹凸),对应的颜色为(0.5,0.5,1),大概就是(127,127,255)那样吧。
法线贴图存储的信息是切线空间的,也就是以模型顶点为中心,x轴为切线,y轴为副切线,z轴为法线,也就是说为啥(0,0,1)对应的是正常的法线了。
编写Shader的时候,用到方向的都写成3维的向量,不要用4维的,一是不需要(不用平移,不需要受w分量的影响),二是会出错(减少出错的几率)
Shader "chicai/7"{ Properties{ _Specular("Specular",Color)=(1,1,1,1) _Glass("Glass",Range(8,20))=8 _MainTex("MainTex",2D)="white"{} _NormalTex("NormalTex",2D)="white"{} _NormalIndexd("AoTuQiangDu",float)=1 } SubShader{ Pass{ Tags{"RenderMode"="ForwardBase"} CGPROGRAM fixed4 _Diffuse; fixed4 _Specular; float _Glass; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalTex; float4 _NormalTex_ST; float _NormalIndexd; #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; float4 tangent:TANGENT; float4 texcoord:TEXCOORD0; }; struct v2f{ float4 position:SV_POSITION; fixed3 nor:COLOR0; fixed3 lightDir:COLOR1; fixed3 viewDir:COLOR2; float4 uv:TEXCOORD0; }; v2f vert(a2v v){ v2f f; f.position = UnityObjectToClipPos(v.vertex); f.nor = normalize(mul(unity_ObjectToWorld,v.normal)); TANGENT_SPACE_ROTATION; f.lightDir = normalize(mul(rotation,ObjSpaceLightDir(v.vertex))).xyz; f.viewDir = normalize(mul(rotation,ObjSpaceViewDir(v.vertex))).xyz; f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; f.uv.zw = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw; return f; } fixed4 frag(v2f f):SV_Target{ fixed3 tangentNormal = UnpackNormal(tex2D(_NormalTex,f.uv.wz)); tangentNormal.xy = tangentNormal.xy * _NormalIndexd; tangentNormal = normalize(tangentNormal); fixed3 texCol = tex2D(_MainTex,f.uv);//计算改uv上贴图的颜色,当物体本身颜色——Diffuse fixed3 diffuseCol = _LightColor0.rgb * texCol * (dot(tangentNormal,f.lightDir)/2+0.5); fixed3 aveDir = normalize(f.lightDir+f.viewDir); fixed3 specularCol = _LightColor0.rgb * _Specular * pow(max(dot(aveDir,tangentNormal),0),_Glass); fixed3 allColor = diffuseCol + specularCol; return fixed4(allColor,1); } ENDCG } } }
Unity属性中添加法线贴图属性和凹凸强度系数
_NormalTex("NormalTex",2D)="white"{} _NormalIndexd("AoTuQiangDu",float)=1
CG声明对应的变量和平移缩放变量
sampler2D _NormalTex; float4 _NormalTex_ST; float _NormalIndexd;
引进切线到顶点函数float4 tangent:TANGENT;
使用传入片元的uv参数的xy传递贴图uv坐标,zw传递法线贴图的uv坐标
f.uv.zw = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw;
转变视野方向和光线方向到切线空间
TANGENT_SPACE_ROTATION;//一个宏,计算出rotation f.lightDir = normalize(mul(rotation,ObjSpaceLightDir(v.vertex))).xyz; f.viewDir = normalize(mul(rotation,ObjSpaceViewDir(v.vertex))).xyz;
片元中得出相应的法线
fixed3 tangentNormal = UnpackNormal(tex2D(_NormalTex,f.uv.wz));//像素图片(0-255)转化为方向的(-1,1) tangentNormal.xy = tangentNormal.xy * _NormalIndexd; tangentNormal = normalize(tangentNormal);
Shader "chicai/6"{ Properties{ _Specular("Specular",Color)=(1,1,1,1) _Glass("Glass",Range(8,20))=8 _MainTex("MainTex",2D)="white"{} } SubShader{ Pass{ Tags{"RenderMode"="ForwardBase"} CGPROGRAM fixed4 _Diffuse; fixed4 _Specular; float _Glass; sampler2D _MainTex; float4 _MainTex_ST; #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; float4 texcoord:TEXCOORD0; }; struct v2f{ float4 position:SV_POSITION; fixed3 nor:COLOR0; fixed3 lightDir:COLOR1; fixed3 viewDir:COLOR2; float2 uv:TEXCOORD0; }; v2f vert(a2v o){ v2f f; f.position = UnityObjectToClipPos(o.vertex); f.nor = normalize(mul(unity_ObjectToWorld,o.normal)); f.lightDir = normalize(WorldSpaceLightDir(o.vertex)); f.viewDir = normalize(WorldSpaceViewDir(o.vertex)); f.uv = o.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; return f; } fixed4 frag(v2f f):SV_Target{ fixed3 texCol = tex2D(_MainTex,f.uv);//计算该uv上贴图的颜色,当物体本身颜色——Diffuse fixed3 diffuseCol = _LightColor0.rgb * texCol * (dot(f.nor,f.lightDir)/2+0.5); fixed3 aveDir = normalize(f.lightDir+f.viewDir);//光照方向和视野方向的平分线 fixed3 specularCol = _LightColor0.rgb * _Specular * pow(max(dot(aveDir,f.nor),0),_Glass); fixed3 allColor = diffuseCol + specularCol; return fixed4(allColor,1); } ENDCG } } }
首先,Unity变量:
_MainTex("MainTex",2D)="white"{}
对应CG变量:
sampler2D _MainTex; float4 _MainTex_ST;//固定,变量名_ST,xy分量表示贴图的缩放,zw分量表示贴图的偏移
顶点函数传入uv纹理坐标 float4 texcoord:TEXCOORD0;
计算出含偏移缩放等的纹理坐标给片元
float2 uv:TEXCOORD0; f.uv = o.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;//乘缩放,+偏移 f.uv = TRANSFORM_TEX(v.texcoord, _MainTex_ST);//内置的方法转换uv,等同于上面一行
找出对应uv坐标对应的纹理颜色充当漫反射自身颜色
// 摄像机方向(视角方向) float3 WorldSpaceViewDir(float4 v) // 根据模型空间中的顶点坐标 得到 (世界空间)从这个点到摄像机的观察方向 float3 UnityWorldSpaceViewDir(float4 v) // 世界空间中的顶点坐标==》世界空间从这个点到摄像机的观察方向 float3 ObjSpaceViewDir(float4 v) // 模型空间中的顶点坐标==》模型空间从这个点到摄像机的观察方向 // 光源方向 float3 WorldSpaceLightDir(float4 v) // 模型空间中的顶点坐标==》世界空间中从这个点到光源的方向 float3 UnityWorldSpaceLightDir(float4 v) // 世界空间中的顶点坐标==》世界空间中从这个点到光源的方向 float3 ObjSpaceLightDir(float4 v) // 模型空间中的顶点坐标==》模型空间中从这个点到光源的方向 // 方向转换 float3 UnityObjectToWorldNormal(float3 norm) // 把法线方向 模型空间==》世界空间 float3 UnityObjectToWorldDir(float3 dir) // 把方向 模型空间=》世界空间 float3 UnityWorldToObjectDir(float3 dir) // 把方向 世界空间=》模型空间