2.1.4 Shading in World Space (about uniforms) 在世界坐标系里着色(关于uniforms参数)

Some chameleons are able to change their color according to the world around them.


This tutorial introducesuniform parameters. It assumes that you are familiar withSection “Minimal Shader”, Section “RGB Cube”, and Section “Debugging of Shaders”.

In this tutorial we will look at a shader that changes the fragment color depending on its position in the world. The concept is not too complicated; however, there are extremely important applications, e.g. shading with lights and environment maps. We will also have a look at shaders in the real world; i.e., what is necessary to enable non-programmers to use your shaders?

1.Transforming from Object to World Space

As mentioned in Section “Debugging of Shaders”, the vertex input parameter with semantic POSITION specifies object coordinates, i.e. coordinates in the local object (or model) space of a mesh. The object space (or object coordinate system) is specific to each game object; however, all game objects are transformed into one common coordinate system — the world space.

If a game object is put directly into the world space, the object-to-world transformation is specified by the Transform component of the game object. To see it, select the object in theScene View or the Hierarchy View and then find the Transform component in the Inspector View. There are parameters for “Position”, “Rotation” and “Scale” in the Transform component, which specify how vertices are transformed from object coordinates to world coordinates. (If a game object is part of a group of objects, which is shown in the Hierarchy View by means of indentation, then the Transform component only specifies the transformation from object coordinates of a game object to the object coordinates of the parent. In this case, the actual object-to-world transformation is given by the combination of the transformation of a object with the transformations of its parent, grandparent, etc.) The transformations of vertices by translations, rotations and scalings, as well as the combination of transformations and their representation as 4×4 matrices are discussed inSection “Vertex Transformations”.

Back to our example: the transformation from object space to world space is put into a 4×4 matrix, which is also known as “model matrix” (since this transformation is also known as “model transformation”). This matrix is available in the uniform parameter _Object2World, which is automatically defined by Unity in this way:

   uniform float4x4 _Object2World;
Since it is automatically defined, we don't need to define it (actually we must not). Instead we can use the uniform parameter _Object2World without definition in the following shader:

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

         #pragma vertex vert  
         #pragma fragment frag 
 
         // uniform float4x4 _Object2World; 
            // 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 =  mul(UNITY_MATRIX_MVP, input.vertex);
            output.position_in_world_space = 
               mul(_Object2World, input.vertex);
               // transformation of input.vertex from object 
               // coordinates to world coordinates;
            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR 
         {
             float dist = distance(input.position_in_world_space, 
               float4(0.0, 0.0, 0.0, 1.0));
               // computes the distance between the fragment position 
               // and the origin (the 4th coordinate should always be 
               // 1 for points).
            
            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  
      }
   }
}

Usually, the application has to set the value of uniform parameters; however, Unity takes care of always setting the correct value of predefined uniform parameters such as_Object2World; thus, we don't have to worry about it.

This shader transforms the vertex position to world space and gives it to the fragment shader in the output structure. For the fragment shader, the parameter in the output structure contains the interpolated position of the fragment in world coordinates. Based on the distance of this position to the origin of the world coordinate system, one of two colors is set. Thus, if you move an object with this shader around in the editor it will turn green near the origin of the world coordinate system. Farther away from the origin it will turn dark grey.

2.More Unity-Specific Uniforms

There are several built-in uniform parameters that are automatically defined by Unity similarly to thefloat4x4 matrix _Object2World. Here is a short list of uniforms (including _Object2World) that are used in several tutorials:

   uniform float4 _Time, _SinTime, _CosTime; // time values
   uniform float4 _ProjectionParams;
      // x = 1 or -1 (-1 if projection is flipped)
      // 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 _Object2World; // model matrix
   uniform float4x4 _World2Object; // inverse model matrix 
   uniform float4 _LightPositionRange; // xyz = pos, w = 1/range
   uniform float4 _WorldSpaceLightPos0; 
      // position or direction of light source

   uniform float4x4 UNITY_MATRIX_MVP; // model view projection matrix 
   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
   uniform float4x4 UNITY_MATRIX_IT_MV; 
      // transpose of the inverse model view matrix
   uniform float4x4 UNITY_MATRIX_TEXTURE0; // texture matrix
   uniform float4x4 UNITY_MATRIX_TEXTURE1; // texture matrix
   uniform float4x4 UNITY_MATRIX_TEXTURE2; // texture matrix
   uniform float4x4 UNITY_MATRIX_TEXTURE3; // texture matrix
   uniform float4 UNITY_LIGHTMODEL_AMBIENT; // ambient color

For the official list of Unity's built-in uniforms, see the section“Built-in shader variables” in Unity's manual.

Some of these uniforms are actually defined in the file UnityShaderVariables.cginc which is automatically included since version 4.0 of Unity.

There are also some built-in uniforms that are not automatically defined, e.g. _LightColor0, which is defined in Lighting.cginc. Thus, we have to either define it explicitly (if it is needed):

   uniform float4 _LightColor0;
or include the file with its definition:
   #include "Lighting.cginc"

Unity does not always update all of these uniforms. In particular, _WorldSpaceLightPos0 and_LightColor0 are only set correctly for shader passes that are tagged appropriately, e.g. withTags {"LightMode" = "ForwardBase"} as the first line in the Pass {...} block; see also Section “Diffuse Reflection”.

3.User-Specified Uniforms: Shader Properties

There is one more important type of uniform parameters: uniforms that can be set by the user. Actually, these are called shader properties in Unity. You can think of them as user-specified uniform parameters of the shader. A shader without parameters is usually used only by its programmer because even the smallest necessary change requires some programming. On the other hand, a shader using parameters with descriptive names can be used by other people, even non-programmers, e.g. CG artists. Imagine you are in a game development team and a CG artist asks you to adapt your shader for each of 100 design iterations. It should be obvious that a few parameters, which even a CG artist can play with, might save you a lot of time. Also, imagine you want to sell your shader: parameters will often dramatically increase the value of your shader.

Since the description of shader properties in Unity's ShaderLab reference is quite OK, here is only an example, how to use shader properties in our example. We first declare the properties and then define uniforms of the same names and corresponding types.

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  
      }
   }
}
With these parameters, a non-programmer can modify the effect of our shader. This is nice; however, the properties of the shader (and in fact uniforms in general) can also be set by scripts! For example, a JavaScript attached to the game object that is using the shader can set the properties with these lines:
   GetComponent(Renderer).sharedMaterial.SetVector("_Point", 
      Vector4(1.0, 0.0, 0.0, 1.0));
   GetComponent(Renderer).sharedMaterial.SetFloat("_DistanceNear", 
      10.0);
   GetComponent(Renderer).sharedMaterial.SetColor("_ColorNear", 
      Color(1.0, 0.0, 0.0));
   GetComponent(Renderer).sharedMaterial.SetColor("_ColorFar", 
      Color(1.0, 1.0, 1.0));
GetComponent(Renderer) returns theRenderer component. (You could also writeGetComponent.<Renderer>() or (less efficient) GetComponent("Renderer").) UsesharedMaterial if you want to change the parameters for all objects that use this material and just material if you want to change the parameters only for one object. (But be aware thatmaterial might create a new instance of the material, which is not automatically destroyed when the game object is destroyed.) With scripting you could, for example, set the_Point to the position of another object (i.e. the position of its Transform component). In this way, you can specify a point just by moving another object around in the editor. In order to write such a script, select Create > JavaScript in the Project View and copy & paste this code:
@script ExecuteInEditMode() // make sure to run in edit mode

var other : GameObject; // another user-specified object

function Update () // this function is called for every frame
{
   if (null != other) // has the user specified an object?
   {
      GetComponent(Renderer).sharedMaterial.SetVector("_Point", 
         other.transform.position); // set the shader property 
         // _Point to the position of the other object
   }
}

Then, you should attach the script to the object with the shader (e.g., by dragging & dropping the script over the object) and drag & drop another object to the other variable of the script in the Inspector View. Now you can change the value of the _Point variable of the material by changing the position of the other object.

4.Summary

Congratulations, you made it! We discussed:

  • How to transform a vertex into world coordinates.
  • The most important Unity-specific uniforms that are supported by Unity.
  • How to make a shader more useful and valuable by adding shader properties.

5.Further Reading

If you want to know more


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值