[UnityShader2]顶点片段着色器实例(五)

来源:互联网 发布:淘宝逆战代刷 编辑:程序博客网 时间:2024/04/29 16:00

官方文档: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        }    }}

2.

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        }    }}


例子3中的那幅图是使用模型自带法线,而这个例子使用的是法线贴图中的法线,很明显,细节更多了(例如有更多的折痕)


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        }    }}


0 0
原创粉丝点击