您的位置:首页 > 移动开发 > Unity3D

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

2016-03-20 16:09 609 查看
官方文档: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
}
}
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  顶点片段着色器