在《在着色器中调试》章节中提到,顶点输入参数使用语义词 POSITION 指定对象的坐标,即本地对象(模型)的网格空间坐标,对象空间(对象的坐标系统)是特定于每个游戏对象的,然而,所有的游戏对象会被转换到一个共同的坐标系中(世界空间)。
如果一个对象被直接放入世界空间中,游戏对象会通过转换组件直接将对象的坐标转成世界坐标。你可以在 Scene 视图或者 Hierarchy 视图中选中一个对象,然后在 Inspector 视图中查看 Transform 组件。在 Transform 组件中包括 Position、Rotation 和 Scale 属性,这个组件用来指定从本地坐标到世界坐标的顶点转换(如果一个游戏对象拥有父级对象,那么转换组件只转换父级对象的坐标)。在《Vertex Transformations》章节中,我们将讨论顶点的转换、旋转、缩放以及 4 x 4 矩阵组合变换的细节。
回到我们的例子中:从对象空间到世界空间的转换是通过 4 x 4 矩阵来转换的,也叫“模型矩阵”,这个矩阵在 Unity 中通过 uniform 参数 _Object2World 已经声明了:
(资料图)
Shader "Custom/World Space"
{ SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag //uniform flaot4*4 Object3World //automatic definition of a Unity-specific uniform parameter 自动定义统一值是unity的一个特性 struct vertexOutput { float4 pos: SV_POSITION; float4 position_in_world_space : TEXCOORD0; }; vertexOutput vert(float4 vertex:POSITION) { vertexOutput output; output.pos = mul(UNITY_MATRIX_MVP,vertex); output.position_in_world_space = mul(_Object2World,vertex);//物体坐标转化为世界坐标 return output; } float4 frag(vertexOutput input) :COLOR { float dist = distance(input.position_in_world_space,float4(0.0,0.0,0.0,1.0)); //计算片段位置与原点距离(第四个坐标永远是1) if (dist < 5.0) { return float4(0.0, 1.0, 0.0, 1.0); //接近 } else return float4(0.3,0.3,0.3,1.0);//远离原点 } ENDCG } }
}
更多的 Unity Uniforms
在 Unity 中,定义了几个和 _Object2World 一样的 float4x4 矩阵,下面是在系列教程中使用的 uniforms 简短列表:
uniform float4 _Time, _SinTime, _CosTime; // time valuesuniform float4 _ProjectionParams;// x = 1 or -1 (-1 if projection is flipped)// y = near plane; z = far plane; w = 1/far planeuniform float4 _ScreenParams; // x = width; y = height; z = 1 + 1/width; w = 1 + 1/heightuniform float4 unity_Scale; // w = 1/scale; see _World2Objectuniform float3 _WorldSpaceCameraPos;uniform float4x4 _Object2World; // model matrixuniform float4x4 _World2Object; // inverse model matrix // (all but the bottom-right element have to be scaled // with unity_Scale.w if scaling is important) uniform float4 _LightPositionRange; // xyz = pos, w = 1/rangeuniform float4 _WorldSpaceLightPos0; // position or direction of light sourceuniform float4x4 UNITY_MATRIX_MVP; // model view projection matrix uniform float4x4 UNITY_MATRIX_MV; // model view matrixuniform float4x4 UNITY_MATRIX_V; // view matrixuniform float4x4 UNITY_MATRIX_P; // projection matrixuniform float4x4 UNITY_MATRIX_VP; // view projection matrixuniform float4x4 UNITY_MATRIX_T_MV; // transpose of model view matrixuniform float4x4 UNITY_MATRIX_IT_MV; // transpose of the inverse model view matrixuniform float4x4 UNITY_MATRIX_TEXTURE0; // texture matrixuniform float4x4 UNITY_MATRIX_TEXTURE1; // texture matrixuniform float4x4 UNITY_MATRIX_TEXTURE2; // texture matrixuniform float4x4 UNITY_MATRIX_TEXTURE3; // texture matrixuniform float4 UNITY_LIGHTMODEL_AMBIENT; // ambient color
用户指定 Uniforms:着色器属性
还有一个更重要的 uniform 参数:用户自定义的 uniforms。实际上,这是 Unity 的属性,你可以认为他们是着色器的用户自定义 uniform 参数。通常一个着色器不带参数只能由编写的程序员在一些特定的程序中使用,但是如果一个着色器拥有参数并且还带有描述性的说明,那么这个着色器就可以被其他人使用,另外,如果你打算出售你的着色器,为着色器的提供参数会大大增加它的价值。
因为在 Unity 的 ShaderLab 中使用《description of shader properties 》非常不错,通过下面的例子我们来了解如何使用着色器属性,首先,我们声明一个属性,然后我们再定义一个与属性名称相同、类型相同的 uniforms 。
Shader "Cg shading in world space" { Properties { _Point ("a point in world space", Vector) = (0., 0., 0., 1.0) _DistanceNear ("threshold distance", Float) = 5.0 _ColorNear ("color near to point", Color) = (0.0, 1.0, 0.0, 1.0) _ColorFar ("color far from point", Color) = (0.3, 0.3, 0.3, 1.0) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" // defines _Object2World and _World2Object // uniforms corresponding to properties uniform float4 _Point; uniform float _DistanceNear; uniform float4 _ColorNear; uniform float4 _ColorFar; struct vertexInput { float4 vertex : POSITION; }; struct vertexOutput { float4 pos : SV_POSITION; float4 position_in_world_space : TEXCOORD0; }; vertexOutput vert(vertexInput input) { vertexOutput output; output.pos = mul(UNITY_MATRIX_MVP, input.vertex); output.position_in_world_space = mul(_Object2World, input.vertex); return output; } float4 frag(vertexOutput input) : COLOR { float dist = distance(input.position_in_world_space, _Point); // computes the distance between the fragment position // and the position _Point. if (dist < _DistanceNear) { return _ColorNear; } else { return _ColorFar; } } ENDCG } }}
使用 sharedMaterial 我们可以改变所有使用了这个材质的对象的参数,如果你只希望改变一个并使用了这个材质的对象的参数,那么你应该使用 material 。假如你设置 _Point 属性为另一个对象的位置信息,这样你只需要在 Unity 中移动这个对象就可以查看效果了,你可以复制/粘贴 下面的代码到一个 C# 脚本中:
using UnityEngine;using System.Collections;[ExecuteInEditMode]public class NewBehaviourScript : MonoBehaviour {public GameObject other;void Update(){if(other != null){GetComponent().sharedMaterial.SetVector("_Point", other.transform.position);}}}
然后我们只需要移动另一个对象(Sphere)就可以看到(Cube)颜色的变化,当小球离原点近时,如图:
当小球离原点远时,如图: