2.1.2 RGB Cube (about vertex output parameters) RGB立方体(关于顶点输出参数)

An RGB cube: the x, y, z coordinates are mapped to red, green, and blue color components.


This tutorial discusses vertex output parameters and fragment input parameters. It is assumed that you are familiar with Section “Minimal Shader”.

In this tutorial we will write a shader to render an RGB cube similar to the one shown to the left. The color of each point on the surface is determined by its coordinates; i.e., a point at position(x,y,z) has the color(\text{red}, \text{green}, \text{blue}) = (x,y,z). For example, the point(x,y,z)=(0,0,1) is mapped to the color(\text{red}, \text{green}, \text{blue}) = (0,0,1), i.e. pure blue. (This is the blue corner in the lower right of the figure to the left.)

1.Preparations

Since we want to create an RGB cube, you first have to create a cube game object. As described inSection “Minimal Shader” for a sphere, you can create a cube game object by selecting GameObject > 3D Object > Cube from the main menu. Continue with creating a material and a shader object and attaching the shader to the material and the material to the cube as described in Section “Minimal Shader”.

2.The Shader Code

Here is the shader code, which you should copy & paste into your shader object:

Shader "Cg shader for RGB cube" { 
   SubShader { 
      Pass { 
         CGPROGRAM 
 
         #pragma vertex vert // vert function is the vertex shader 
         #pragma fragment frag // frag function is the fragment shader
 
         // for multiple vertex output parameters an output structure 
         // is defined:
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : TEXCOORD0;
         };
 
         vertexOutput vert(float4 vertexPos : POSITION) 
            // vertex shader 
         {
            vertexOutput output; // we don't need to type 'struct' here
 
            output.pos =  mul(UNITY_MATRIX_MVP, vertexPos);
            output.col = vertexPos + float4(0.5, 0.5, 0.5, 0.0);
               // Here the vertex shader writes output data
               // to the output structure. We add 0.5 to the 
               // x, y, and z coordinates, because the 
               // coordinates of the cube are between -0.5 and
               // 0.5 but we need them between 0.0 and 1.0. 
            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR // fragment shader
         {
            return input.col; 
               // Here the fragment shader returns the "col" input 
               // parameter with semantic TEXCOORD0 as nameless
               // output parameter with semantic COLOR.
         }
 
         ENDCG  
      }
   }
}

If your cube is not colored correctly, check the console for error messages (by selecting Window > Console from the main menu), make sure you have saved the shader code, and check whether you have attached the shader object to the material object and the material object to the game object.

3.Communication between Vertex and Fragment Shaders

The main task of our shader is to set the fragment output color (i.e. the fragment output parameter with semanticCOLOR) in the fragment shader to the vertex position that is available in the vertex shader. Actually, this is not quite true: the coordinates in the vertex input parameter with semantic POSITION for Unity's default cube are between -0.5 and +0.5 while we would like to have color components between 0.0 and 1.0; thus, we need to add 0.5 to the x, y, and z component, which is done by this expression: vertexPos + float4(0.5, 0.5, 0.5, 0.0).

The main problem, however, is: how do we get any value from the vertex shader to the fragment shader? It turns out that the only way to do this is to use pairs of vertex output parameters and fragment input parameters with the same semantics (TEXCOORD0 in this case). In fact, it is only the semantics that are used to determine which vertex output parameters correspond to which fragment input parameters. Instead of the semantic TEXCOORD0 we could also use another semantic, e.g. COLOR, it doesn't really matter here, except that parameters with the semanticCOLOR are often clamped to values between 0 and 1 (which would be OK in this case). It is, however, common to use the semanticsTEXCOORD0, TEXCOORD1, TEXCOORD2, etc. for all kinds of parameters.

The next problem is to specify multiple vertex output parameters. Since the return instruction can only return one value, it is common to define a structure for all the required vertex output parameters. Here, this structure is called vertexOutput:

         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : TEXCOORD0;
         };
By using this structure as an argument of the fragment shader function, we make sure that the semantics match. Note that in Cg (in contrast to C), we don't have to writestruct vertexOutput when defining variables of this type but we can just use the namevertexOutput (withoutstruct) for the same type.

4.The out Qualifier

An alternative to the use of an output structure would be to use arguments of the vertex shader function with theout qualifier, e.g.:

Shader "Cg shader for RGB cube" { 
   SubShader { 
      Pass { 
         CGPROGRAM 
 
         #pragma vertex vert // vert function is the vertex shader 
         #pragma fragment frag // frag function is the fragment shader
 
         void vert(float4 vertexPos : POSITION,
            out float4 pos : SV_POSITION,
            out float4 col : TEXCOORD0)  
         {
            pos =  mul(UNITY_MATRIX_MVP, vertexPos);
            col = vertexPos + float4(0.5, 0.5, 0.5, 0.0);
            return;
         }
 
         float4 frag(float4 pos : SV_POSITION, 
            float4 col : TEXCOORD0) : COLOR 
         {
            return col; 
         }
 
         ENDCG 
      }
   }
}

However, the use of an output structure is more common in practice and it makes sure that vertex output parameters and fragment input parameters have matching semantics.

5.Variations of this Shader

The RGB cube represents the set of available colors (i.e. the gamut of the display). Thus, it can also be used show the effect of a color transformation. For example, a color to gray transformation could compute the mean of the red, green, and blue color components, i.e. (\text{red}+\text{green}+\text{blue}) / 3, and then put this value in all three color components of the fragment color to obtain a gray value of the same brightness. Instead of the mean, the relative luminance could also be used, which is0.21 \text{ red} + 0.72 \text{ green} + 0.07 \text{ blue}. Of course, any other color transformation (changing saturation, contrast, hue, etc.) is also applicable.

Another variation of this shader could compute a CMY (cyan, magenta, yellow) cube: for position(x, y, z) you could subtract from a pure white an amount of red that is proportional tox in order to produce cyan. Furthermore, you would subtract an amount of green in proportion to the y component to produce magenta and also an amount of blue in proportion to z to produce yellow.

If you really want to get fancy, you could compute an HSV (hue, saturation, value) cylinder. Forx andz coordinates between -0.5 and +0.5, you can get an angleH between 0 and 360° with 180.0+degrees(atan2(z, x)) in Cg and a distanceS between 0 and 1 from they axis with2.0 * sqrt(x * x + z * z). The y coordinate for Unity's built-in cylinder is between -1 and 1 which can be translated to a value V between 0 and 1 by(y + 1.0) / 2.0. The computation of RGB colors from HSV coordinates is described in the article on HSV in Wikipedia.

6.Interpolation of Vertex Output Parameters

The story about vertex output parameters and fragment input parameters is not quite over yet. If you select the cube game object, you will see in the Scene View that it consists of only 12 triangles and 8 vertices. Thus, the vertex shader might be called only eight times and only eight different outputs are written to the vertex output parameters. However, there are many more colors on the cube. How did that happen?

In fact, the vertex shader is only called for each vertex of each triangle. However, the different values of the vertex output parameters for the different vertices are interpolated across the triangle. The fragment shader is then called for each pixel that is covered by the triangle and receives interpolated values of the vertex output parameters as fragment input parameters. The details of this interpolation are described inSection “Rasterization”.

If you want to make sure that a fragment shader receives one exact, non-interpolated value by a vertex shader, you have to make sure that the vertex shader writes the same value to the vertex output parameters for all vertices of a triangle.

7.Summary

And this is the end of this tutorial. Congratulations! Among other things, you have seen:

  • What an RGB cube is.
  • What an output structure is and how to define it.
  • How output structures are used to make sure that vertex output parameters have the same semantics as fragment input parameters.
  • How the values written to vertex output parameters are interpolated across a triangle before they are received as input parameters by the fragment shader.

8.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、付费专栏及课程。

余额充值