官方文档:http://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html
相关链接:http://blog.csdn.net/candycat1992/article/details/41605257
1.
a.cg内置函数:tex2D(sample2D tex, float2 s) //s为纹理(uv)坐标
b.UnityCG.cginc:TRANSFORM_TEX,其定义为:
// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
##是字符串连接符,同时需要定义一个带有_ST的变量。其中name##_ST.xy是缩放倍数,name##_ST.zw是偏移值
Shader "Unlit/NewUnlitShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }
a.UnityCG.cginc:UnityObjectToWorldNormal,其定义为:
// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
{
// Multiply by transposed inverse matrix, actually using transpose() generates badly optimized code
return normalize(_World2Object[0].xyz * norm.x + _World2Object[1].xyz * norm.y + _World2Object[2].xyz * norm.z);
}
一般来说,需要将模型空间的法线转换为世界空间的法线,法线是float3类型的,与4x4的矩阵是不能直接相乘的
Shader "Unlit/WorldSpaceNormals" { // no Properties block this time! SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // include file that contains UnityObjectToWorldNormal helper function #include "UnityCG.cginc" struct v2f { // we'll output world space normal as one of regular ("texcoord") interpolators half3 worldNormal : TEXCOORD0; float4 pos : SV_POSITION; }; // vertex shader: takes object space normal as input too v2f vert (float4 vertex : POSITION, float3 normal : NORMAL) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, vertex); // UnityCG.cginc file contains function to transform // normal from object to world space, use that o.worldNormal = UnityObjectToWorldNormal(normal); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 c = 0; // normal is a 3D vector with xyz components; in -1..1 // range. To display it as color, bring the range into 0..1 // and put into red, green, blue components c.rgb = i.worldNormal*0.5+0.5; return c; } ENDCG } } }
3.使用世界空间的法线进行环境反射(天空盒)
a.HDR:http://www.ceeger.com/Manual/HDR.html
b.cg内置函数:reflect(I, N),根据入射光线方向I和表面法向量N计算反射向量,仅对三元向量有效,一般来说都统一在世界空间中进行计算
c.cg内置函数:normalize(v),返回一个指向与向量v一样,长度为1的向量
d.UnityCG.cginc:UnityWorldSpaceViewDir,其定义为:
// Computes world space view direction, from object space position
inline float3 UnityWorldSpaceViewDir( in float3 worldPos )
{
return _WorldSpaceCameraPos.xyz - worldPos;
}
e.当天空盒在场景中使用时,会被当作为一个反射源,然后unity内部会创建一个默认的反射探头(Reflection Probe),这个探头包含了天空盒的数据。因为天空盒本质上可以看作为一个Cubemap(6个面),即探头包含了一个Cubemap的数据。
Shader "Unlit/SkyReflection" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { half3 worldRefl : TEXCOORD0; float4 pos : SV_POSITION; }; v2f vert (float4 vertex : POSITION, float3 normal : NORMAL) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, vertex); // compute world space position of the vertex float3 worldPos = mul(_Object2World, vertex).xyz; // compute world space view direction float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); // world space normal float3 worldNormal = UnityObjectToWorldNormal(normal); // world space reflection vector o.worldRefl = reflect(-worldViewDir, worldNormal); return o; } fixed4 frag (v2f i) : SV_Target { // sample the default reflection cubemap, using the reflection vector half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl); // decode cubemap data into actual color half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR); // output it! fixed4 c = 0; c.rgb = skyColor; return c; } ENDCG } } }
4.使用法线贴图进行环境反射
a.通常来说,法线贴图用来为物体添加细节(凹凸感),而不会增添额外的几何体。上面的shader,反射方向是逐顶点计算的;而如果我们要使用法线贴图,那么贴图表面上的法线就需要逐像素计算(对于与贴图相关的计算,一般要放在片段程序进行逐像素计算)。
b.切线空间:在切线空间中,原点就是顶点的位置,z轴就是该顶点法线的方向,另外两个轴就是与该顶点相切的两条切线。理论上切线是有无数条的,但模型一般会给定该顶点的一条切线方向,这个切线方向一般是使用和纹理坐标方向相同的那条切线。而另一个坐标轴的方向就可以通过normal和tangent的叉乘得到。这三个坐标轴依次称为normal、tangent、bitangent,简写为N、T、B。在顶点程序的输入中,我们就已经可以得到法线和切线这两个值了(模型空间下的)。
法线贴图一般就是像这样一片蓝色的贴图了:
c.法线贴图存储的是在切线空间下的法线值,并且该值是一个"压缩值"。因为法线在(-1,1)范围,要映射到(0,1)范围,要进行"压缩"。那么,如果我们使用tex2D函数进行采样,对采样之后的值就要进行"解压",使用的就是UnityCG.cginc下的UnpackNormal函数:inline fixed3 UnpackNormal(fixed4 packednormal)。得到这个"解压"后的切线空间下的法线值,我们需要把它转换为世界空间,这样就能进行统一的计算了。说道转换空间,那么就要搞一个矩阵,能将向量从切线空间转到世界空间。
Shader "Unlit/SkyReflection Per Pixel" { Properties { // normal map texture on the material, // default to dummy "flat surface" normalmap _BumpMap("Normal Map", 2D) = "bump" {} } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float3 worldPos : TEXCOORD0; // these three vectors will hold a 3x3 rotation matrix // that transforms from tangent to world space half3 tspace0 : TEXCOORD1; // tangent.x, bitangent.x, normal.x half3 tspace1 : TEXCOORD2; // tangent.y, bitangent.y, normal.y half3 tspace2 : TEXCOORD3; // tangent.z, bitangent.z, normal.z // texture coordinate for the normal map float2 uv : TEXCOORD4; float4 pos : SV_POSITION; }; // vertex shader now also needs a per-vertex tangent vector. // in Unity tangents are 4D vectors, with the .w component used to // indicate direction of the bitangent vector. // we also need the texture coordinate. v2f vert (float4 vertex : POSITION, float3 normal : NORMAL, float4 tangent : TANGENT, float2 uv : TEXCOORD0) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, vertex); o.worldPos = mul(_Object2World, vertex).xyz; half3 wNormal = UnityObjectToWorldNormal(normal); half3 wTangent = UnityObjectToWorldDir(tangent.xyz); // compute bitangent from cross product of normal and tangent half tangentSign = tangent.w * unity_WorldTransformParams.w; half3 wBitangent = cross(wNormal, wTangent) * tangentSign; // output the tangent space matrix o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x); o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y); o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z); o.uv = uv; return o; } // normal map texture from shader properties sampler2D _BumpMap; fixed4 frag (v2f i) : SV_Target { // sample the normal map, and decode from the Unity encoding half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv)); // transform normal from tangent to world space half3 worldNormal; worldNormal.x = dot(i.tspace0, tnormal); worldNormal.y = dot(i.tspace1, tnormal); worldNormal.z = dot(i.tspace2, tnormal); // rest the same as in previous shader half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); half3 worldRefl = reflect(-worldViewDir, worldNormal); half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl); half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR); fixed4 c = 0; c.rgb = skyColor; return c; } ENDCG } } }
5.进一步改进
Shader "Unlit/More Textures" { Properties { // three textures we'll use in the material _MainTex("Base texture", 2D) = "white" {} _OcclusionMap("Occlusion", 2D) = "white" {} _BumpMap("Normal Map", 2D) = "bump" {} } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" // exactly the same as in previous shader struct v2f { float3 worldPos : TEXCOORD0; half3 tspace0 : TEXCOORD1; half3 tspace1 : TEXCOORD2; half3 tspace2 : TEXCOORD3; float2 uv : TEXCOORD4; float4 pos : SV_POSITION; }; v2f vert (float4 vertex : POSITION, float3 normal : NORMAL, float4 tangent : TANGENT, float2 uv : TEXCOORD0) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, vertex); o.worldPos = mul(_Object2World, vertex).xyz; half3 wNormal = UnityObjectToWorldNormal(normal); half3 wTangent = UnityObjectToWorldDir(tangent.xyz); half tangentSign = tangent.w * unity_WorldTransformParams.w; half3 wBitangent = cross(wNormal, wTangent) * tangentSign; o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x); o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y); o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z); o.uv = uv; return o; } // textures from shader properties sampler2D _MainTex; sampler2D _OcclusionMap; sampler2D _BumpMap; fixed4 frag (v2f i) : SV_Target { // same as from previous shader... half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv)); half3 worldNormal; worldNormal.x = dot(i.tspace0, tnormal); worldNormal.y = dot(i.tspace1, tnormal); worldNormal.z = dot(i.tspace2, tnormal); half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); half3 worldRefl = reflect(-worldViewDir, worldNormal); half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl); half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR); fixed4 c = 0; c.rgb = skyColor; // modulate sky color with the base texture, and the occlusion map fixed3 baseColor = tex2D(_MainTex, i.uv).rgb; fixed occlusion = tex2D(_OcclusionMap, i.uv).r; c.rgb *= baseColor; c.rgb *= occlusion; return c; } ENDCG } } }