Cg Programming/Unity/Transparent Textures透明纹理

来源:互联网 发布:杭州百丽黛佳网络骗局 编辑:程序博客网 时间:2024/05/21 17:19

本教程涵盖了alpha纹理映射的各种常用用法,也就是RGBA纹理图像,它的A (alpha)分量指定了纹素的不透明度。
它把章节“纹理球体”中的着色器代码和章节“裁剪”及“透明度”中介绍的概念结合起来。
如果你没有阅读过这些教程,这会是一个非常好的机会来了解它们。

丢弃透明片元

这里写图片描述
让我们从章节“裁剪”中解释的片元丢弃开始。遵循章节“纹理球体”中描述的步骤,并且把带有以下着色器的球体的材质指定给上图中。

Shader "Cg texturing with alpha discard" {   Properties {      _MainTex ("RGBA Texture Image", 2D) = "white" {}       _Cutoff ("Alpha Cutoff", Float) = 0.5   }   SubShader {      Pass {             Cull Off // since the front is partially transparent,             // we shouldn't cull the back         CGPROGRAM         #pragma vertex vert           #pragma fragment frag          uniform sampler2D _MainTex;             uniform float _Cutoff;         struct vertexInput {            float4 vertex : POSITION;            float4 texcoord : TEXCOORD0;         };         struct vertexOutput {            float4 pos : SV_POSITION;            float4 tex : TEXCOORD0;         };         vertexOutput vert(vertexInput input)          {            vertexOutput output;            output.tex = input.texcoord;            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);            return output;         }         float4 frag(vertexOutput input) : COLOR         {            float4 textureColor = tex2D(_MainTex, input.tex.xy);              if (textureColor.a < _Cutoff)               // alpha value less than user-specified threshold?            {               discard; // yes: discard this fragment            }            return textureColor;         }         ENDCG      }   }   Fallback "Unlit/Transparent Cutout"}

这个片元着色器读取了RGBA纹理并且把alpha值跟用户自定义的阈值相比较。如果alpha值小于阈值,该片元就会被丢弃并且表面呈现出透明。

注意这个丢弃指令在一些平台上性能相当得慢,特别是在移动设备上。因此,混合通常是一种更有效的替换方法。

混合

章节“透明度”描述了如何用alpha混合来渲染半透明对象。把这个跟RGBA纹理结合起来就有了以下代码:

Shader "Cg texturing with alpha blending" {   Properties {      _MainTex ("RGBA Texture Image", 2D) = "white" {}    }   SubShader {      Tags {"Queue" = "Transparent"}      Pass {             Cull Front // 首先渲染背面         ZWrite Off // 关闭深度缓冲             // 为了不遮挡其它对象         Blend SrcAlpha OneMinusSrcAlpha             // 基于片元的alpha值混合         CGPROGRAM         #pragma vertex vert           #pragma fragment frag          uniform sampler2D _MainTex;         struct vertexInput {            float4 vertex : POSITION;            float4 texcoord : TEXCOORD0;         };         struct vertexOutput {            float4 pos : SV_POSITION;            float4 tex : TEXCOORD0;         };         vertexOutput vert(vertexInput input)          {            vertexOutput output;             output.tex = input.texcoord;            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);            return output;         }         float4 frag(vertexOutput input) : COLOR         {            return tex2D(_MainTex, input.tex.xy);           }         ENDCG      }      Pass {             Cull Back // 现在渲染正面         ZWrite Off // 关闭深度缓冲             // 为了不遮挡其它对象         Blend SrcAlpha OneMinusSrcAlpha            // 基于片元的alpha值混合         CGPROGRAM         #pragma vertex vert           #pragma fragment frag          uniform sampler2D _MainTex;         struct vertexInput {            float4 vertex : POSITION;            float4 texcoord : TEXCOORD0;         };         struct vertexOutput {            float4 pos : SV_POSITION;            float4 tex : TEXCOORD0;         };         vertexOutput vert(vertexInput input)          {            vertexOutput output;            output.tex = input.texcoord;            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);            return output;         }         float4 frag(vertexOutput input) : COLOR         {            return tex2D(_MainTex, input.tex.xy);           }         ENDCG      }   }   Fallback "Unlit/Transparent"}

译者注:片段着色器中计算出来的颜色称之为 “源颜色”,帧缓存中对应像素已经存在的颜色叫做“目标颜色”。

注意所有带有alpha值为0的纹素在这个特殊的纹理图像中是黑色的。实际上,在这个纹理图像中的颜色会跟它们的alpha值“预乘”。(这种颜色也被称为“不透明度的加权”)于是,对于这张特殊的图像,为了避免用混合方程中的alpha值对颜色进行另一种乘法运算,我们应该实际上指定这个混合方程。因此,着色器的一个改进就是在两个通道中使用如下的混合指令:

Blend One OneMinusSrcAlpha //float4(1.0 - fragment_output.a)

用自定义颜色混合

这里写图片描述

在对现有技术没有实际应用的情况下我们不应该结束本教程。上图是有半透明蓝色海洋地球的图像,它是我在维基百科上找到的。有一些照明(或轮廓增强)正在进行,这里我不会再现它。相反,我只想尝试用以下着色器再现半透明海洋的想法,它忽略了纹理贴图的RGB颜色,并且用基于alpha值的指定颜色替换了它们:

Shader "Cg semitransparent colors based on alpha" {   Properties {      _MainTex ("RGBA Texture Image", 2D) = "white" {}    }   SubShader {      Tags {"Queue" = "Transparent"}      Pass {             Cull Front // first render the back faces         ZWrite Off // don't write to depth buffer             // in order not to occlude other objects         Blend SrcAlpha OneMinusSrcAlpha             // blend based on the fragment's alpha value         CGPROGRAM         #pragma vertex vert           #pragma fragment frag          uniform sampler2D _MainTex;         struct vertexInput {            float4 vertex : POSITION;            float4 texcoord : TEXCOORD0;         };         struct vertexOutput {            float4 pos : SV_POSITION;            float4 tex : TEXCOORD0;         };         vertexOutput vert(vertexInput input)          {            vertexOutput output;            output.tex = input.texcoord;            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);            return output;         }         float4 frag(vertexOutput input) : COLOR         {            float4 color =  tex2D(_MainTex, input.tex.xy);              if (color.a > 0.5) // opaque back face?            {               color = float4(0.0, 0.0, 0.2, 1.0);                   // opaque dark blue            }            else // transparent back face?            {               color = float4(0.0, 0.0, 1.0, 0.3);                   // semitransparent green            }            return color;         }         ENDCG      }      Pass {             Cull Back // now render the front faces         ZWrite Off // don't write to depth buffer             // in order not to occlude other objects         Blend SrcAlpha OneMinusSrcAlpha             // blend based on the fragment's alpha value         CGPROGRAM         #pragma vertex vert           #pragma fragment frag          uniform sampler2D _MainTex;         struct vertexInput {            float4 vertex : POSITION;            float4 texcoord : TEXCOORD0;         };         struct vertexOutput {            float4 pos : SV_POSITION;            float4 tex : TEXCOORD0;         };         vertexOutput vert(vertexInput input)          {            vertexOutput output;            output.tex = input.texcoord;            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);            return output;         }         float4 frag(vertexOutput input) : COLOR         {            float4 color = tex2D(_MainTex, input.tex.xy);              if (color.a > 0.5) // opaque front face?            {               color = float4(0.0, 1.0, 0.0, 1.0);                   // opaque green            }            else // transparent front face            {               color = float4(0.0, 0.0, 1.0, 0.3);                   // semitransparent dark blue            }            return color;        }         ENDCG      }   }   Fallback "Unlit/Transparent"}

当然,着色器中加入光照和轮廓增加会有点意思。考虑到纹理颜色,我们也能改变不透明的绿色,也就是:

color = float4(0.5 * color.r, 2.0 * color.g, 0.5 * color.b, 1.0);

which emphasizes the green component by multiplying it with 2 and dims the red and blue components by multiplying them with 0.5. However, this results in oversaturated green that is clamped to the maximum intensity. This can be avoided by halving the difference of the green component to the maximum intensity 1. This difference is 1.0 - color.g; half of it is 0.5 * (1.0 - color.g) and the value corresponding to this reduced distance to the maximum intensity is: 1.0 - 0.5 * (1.0 - color.g). Thus, in order to avoid oversaturation of green, we could use (instead of the opaque green color):
它通过乘以2增强了绿色分量以及通过乘以0.5使红色和蓝色变暗了。但是,这导致了绿色过饱和,它会被限制在最大的强度以内。这个可能通过把绿色分量和最大强度1的差值减半来避免。差值就是1.0 - color.g;它的一半是0.5 * (1.0 - color.g)以及与之对应的到最大强度的等效间距是:1.0 - 0.5 * (1.0 - color.g)。因此,为了避免绿色的过饱和,我们应该使用(替代了不透明绿色):

color = float4(0.5 * color.r, 1.0 - 0.5 * (1.0 - color.g), 0.5 * color.b, 1.0);

实际上,对于这种颜色变换我们必须尝试各种可能性。最后,着色器属性中数字的使用(比如上面代码中的因子0.5)实际上有助于交互开发这些可能性。

总结

恭喜!你又完成了一章的学习!我们学到了:

  • 片元丢弃如何跟alpha纹理映射结合起来。
  • alpha纹理映射如何应用到混合中。
  • 如何用alpha纹理映射决定颜色。
原创粉丝点击