Cg Programming In Shading in World Space(Wiki翻译自用)


一些变色龙能够根据周围的环境改变颜色。本教程介绍uniform参数。
在本教程中,我们将介绍一个着色器,该着色器根据片元在世界中的位置来改变片元的颜色。这个概念并不复杂。但是有非常重要的应用。例如,灯光和环境贴图的阴影。我们还将看一下现实世界中的着色器,即不是程序员能够使用您的着色器需要什么?

从物体空间变换到世界空间

如“Debugging of Shaders”一节中提到的,顶点的输出参数具有POSITION语义指定的是对象坐标,也就是物体自身的空间坐标(也称为模型空间)。对象空间(或者说对象坐标系)是对于每一个游戏对象的。但是所有的游戏对象都变换到一个统一的公共坐标系——世界空间。
如果将游戏对象直接放入世界空间,那么游戏对象的“Transform”组件指的是对象到世界的变换。 要查看它,请在"Scene"窗口或者“Hierarchy”窗口中选择对象,然后在“Inspector”窗口中找到"Transform"组件。在“Transform“组件中有“Position”, “Rotation” 和 “Scale” 参数,用于指定如何将顶点从模型坐标变换到世界坐标。(如果一个游戏对象有父物体,通过缩进显示在”Hierarchy “窗口,那么该物体的”Transform“组件指定的是从该对象的模型坐标到父物体的模型坐标的变换,在这种情况下,实际的模型到世界的变换时通过对象与父物体,祖父母等对象的变换组合而成的)。顶点变换是通过”平移,旋转,缩放“,以及变换的组合而来,他们的表示形式是4x4的矩阵。
回到我们的示例中:从模型空间到世界空间的变换是把他们放到4x4的矩阵中,该矩阵也被称为”模型矩阵“(因此该变换也被称为模型变换)。这个矩阵可直接使用Unity的uniform参数unity_ObjectToWorld使用,该参数由Unity通过以下方式定义:

uniform float4x4 unity_ObjectToWorld;

由于它是自动定义的,因此我们不必定义它(实际上我们必须不定义它)。我们可以使用统一参数unity_ObjectToWorld,而无需定义它:

Shader "Cg shading in world space" {
   SubShader {
      Pass {
         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 
 
         // uniform float4x4 unity_ObjectToWorld; 
            // automatic definition of a Unity-specific uniform parameter

         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 =  UnityObjectToClipPos(input.vertex);
            output.position_in_world_space = 
               mul(unity_ObjectToWorld, input.vertex);
               // 将input.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); 
                  // color near origin
            }
            else
            {
               return float4(0.1, 0.1, 0.1, 1.0); 
                  // color far from origin
            }
         }
 
         ENDCG  
      }
   }
}

通常,应用程序必须设置统一的参数,但是Unity会始终设置正确的预定义统一参数,例如unity_ObjectToWorld,我们不必为此担心。
这个shader将顶点坐标变换到世界坐标,并将其作为顶点的输出结构传给片元着色器。对于片元着色器,输出结构中的参数包含了该片元在世界坐标中的插值位置。根据该位置到世界坐标系原点的位置,设置两种颜色之一。因此,如果在编辑器中四处移动带有此着色器的对象,则它将在世界坐标系的原点附近变为绿色。 离原点较远时,它将变为黑色。

更多的Unity自定义的Uniforms

这里有一些由Unity自定义的内置的uniform参数,类似float4x4类型的矩阵unity_ObjectToWorld。这里是几个教程中使用到的uniforms的简单列表(包括unity_ObjectToWorld):

   uniform float4 _Time, _SinTime, _CosTime; // time values
   uniform float4 _ProjectionParams;
      // x = 1 or -1 (-1 是投影翻转)
      // y = near plane; z = far plane; w = 1/far plane
   uniform float4 _ScreenParams; 
      // x = width; y = height; z = 1 + 1/width; w = 1 + 1/height
   uniform float3 _WorldSpaceCameraPos;
   uniform float4x4 unity_ObjectToWorld; // model matrix模型矩阵
   uniform float4x4 unity_WorldToObject; // inverse model matrix 模型矩阵的逆矩阵
   uniform float4 _WorldSpaceLightPos0; 
      // forward rendering的光源位置或方向

   uniform float4x4 UNITY_MATRIX_MVP; // model view projection matrix 模型视图投影矩阵
      //在一些情况下,UnityObjectToClipPos() 就是使用的这个矩阵
   uniform float4x4 UNITY_MATRIX_MV; // model view matrix
   uniform float4x4 UNITY_MATRIX_V; // view matrix
   uniform float4x4 UNITY_MATRIX_P; // projection matrix
   uniform float4x4 UNITY_MATRIX_VP; // view projection matrix
   uniform float4x4 UNITY_MATRIX_T_MV; 
      // transpose of model view matrix。UNITY_MATRIX_MV矩阵的转置
   uniform float4x4 UNITY_MATRIX_IT_MV; 
      // transpose of the inverse model view matrix
   uniform float4 UNITY_LIGHTMODEL_AMBIENT; // ambient color 环境光照

其中一些uniform实际上是在UnityShaderVariables.cginc文件中定义的,该文件从4.0版本后都包含在Unity中。
还有一些内置的uniform没有被自动定义,例如在UnityLightingCommon.cginc中定义的_LightColor0,我们必须明确定义它(如果需要的话):

 uniform float4 _LightColor0;

或者include定义了它的文件:

#include "UnityLightingCommon.cginc"

或者include一个包含了UnityLightingCommon.cginc文件的文件:

#include "Lighting.cginc"

用户定义的Uniforms:着色器属性

uniform参数还有一种更重要的类型:可由用户设置的uniform。在unity中他们被称为着色器属性。您可以将他们视为着色器的用户指定的uniform参数。没有参数的shader通常仅被程序员使用,因为即使是最小的必要的更改也需要进行一些编程。另一方面,使用带有描述性参数的shader可以由其他人使用,即使他不是程序员,比如CG艺术家。想象您在一个游戏开发团队中,一个CG艺术家要求您为100个设计重复修改您的shader。很明显,那就需要一些CG艺术家能使用的参数,这可以节省您大量的时间。另外,假设您想出售着色器:参数通常会大大增加着色器的价值。
因为Unity对着色器属性的描述文档已经非常好了,因此这里只是一个示例,说明如何在我们的示例中使用着色器属性。我们首先声明属性,然后定义相同名称和相同类型的uniform。

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 unity_ObjectToWorld and unity_WorldToObject

         // 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 =  UnityObjectToClipPos(input.vertex);
            output.position_in_world_space = 
               mul(unity_ObjectToWorld, 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  
      }
   }
}

使用这些参数,非程序员也能修改我们的shader效果。这很好。也可以通过脚本设置着色器的属性(实际上一般是uniform)。例如,附加到游戏对象的shader可以使用c#脚本来设置属性:

GetComponent<Renderer>().sharedMaterial.SetVector("_Point", new Vector4(1.0f, 0.0f, 0.0f, 1.0f));
GetComponent<Renderer>().sharedMaterial.SetFloat("_DistanceNear", 10.0f);
GetComponent<Renderer>().sharedMaterial.SetColor("_ColorNear", new Color(1.0f, 0.0f, 0.0f));
GetComponent<Renderer>().sharedMaterial.SetColor("_ColorFar", new Color(1.0f, 1.0f, 1.0f));

GetComponent<Renderer>()返回Renderer组件。如果你想更改所有使用此材质对象的参数,请使用sharedMaterial;如果只想改变该对象的材质参数,请使用material。(但是请注意,material可能将会创建新的实例材质,当销毁游戏对象时,该实例材质不会自动销毁!)。您可以使用脚本将_Point设置为另一个对象的位置(即另一个对象的Transform组件的position)。这样您可以通过在编辑器中移动另一个物体来指定一个_Point。为了编写一个这样的脚本,请创建一个C#脚本,并将其命名为ShadingInWorldSpace,然后复制粘贴以下代码:

using UnityEngine;

[ExecuteInEditMode, RequireComponent(typeof(Renderer))]
public class ShadingInWorldSpace : MonoBehaviour {
	public GameObject other;
	Renderer rend;
	void Start() {
		rend = GetComponent<Renderer>();
	}

	// Update is called once per frame
	void Update () {
		if(other != null) {
			rend.sharedMaterial.SetVector("_Point", other.transform.position);
		}
	}
}

然后,你可以将脚本拖拽到游戏对象上。现在您可以通过改变一个对象的位置来更改材质的_Point变量

总结

本教程到此结束。本节我们讨论了:

  • 如何将顶点转换为世界坐标
  • Unity支持的主要的unity特定的uniform
  • 如何通过添加着色器属性使着色器更有用和更有价值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值