GodZza

导航

VPOS VS_POSITION ComputeScreenPos 使用场景

VPOS 
VPOS 是比较新的概念,在Unity2018中可以使用(估计Unity5开始的特性?),而在我的旧项目中(使用Unity4.6.4) 上会报错,所以我后来使用了SV_POSITION.xy
但是经过测试 在Unity2018Build Android之后 放在低性能的安卓主板上(3188) 下方的Shader会显示全透明,显然是因为显卡不支持吧。

Shader "Unlit/Screen Position"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            Cull Off
            Lighting Off
            ZWrite Off
            Fog { Mode Off }
            Offset -1, -1
            Blend SrcAlpha OneMinusSrcAlpha
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            // note: no SV_POSITION in this struct
            struct v2f {
                float2 uv : TEXCOORD0;
            };

            v2f vert(
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0, // texture coordinate input
                out float4 outpos : SV_POSITION // clip space position output
                )
            {
                v2f o;
                o.uv = uv;
                //outpos = UnityObjectToClipPos(vertex);
                outpos = UnityObjectToClipPos(vertex);
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
            {
                // screenPos.xy 将包含像素的整数坐标值。
                // 用它们实现棋盘团来,替代渲染4x4的像素块(4x4纹理)

                // 棋盘图案每个格将使用4x4的像素块
                //screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                
                screenPos.xy = floor(screenPos.xy) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);

                // 如果checker为负数,则使用HLSL 内置函数 clip 来停止绘制该像素
                clip(checker);

                // 保持绘制的像素将采样纹理值并输出
                fixed4 c = tex2D(_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}


VS_POSITION 
VS_POSITION 和 POSITION的区别 大家可以自行查找,我用 VS_POSITION 重写了上面的Shader后,在PC可以使用,但是在3188 和 3399上都无法使用(原图输出,没有输出棋盘状)。
其原因是 在PC上, 依然会将各顶点(o.pos)进行插值后的交给 fragment。但是在Android上 获取到 的  pos值是(0,0,0,0) ,也就是说vert之后就已经跳过了pos的处理。

Shader "Custom/ScreenPos" {
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
    Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }
        
        Pass
        {
            Cull Off
            Lighting Off
            ZWrite Off
            Fog { Mode Off }
            Offset -1, -1
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #include "UnityCG.cginc"
            
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata_t
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
    
            struct v2f
            {
                float4 pos : SV_POSITION;
                half2 uv : TEXCOORD0;
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                o.uv = v.uv;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
                
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                // checker value will be negative for 4x4 blocks of pixels
                // in a checkerboard pattern
                float2 screenPos = i.pos.xy;
                screenPos.xy = floor(screenPos.xy) *0.5;// 0.25;
                float checker = -frac(screenPos.x + screenPos.y); 

                // clip HLSL instruction stops rendering a pixel if value is negative
                //clip(checker);
                
                if(checker < 0)
                    return fixed4(0,0,0,0);

                // for pixels that were kept, read the texture and output it
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
                
            }
            ENDCG
        }
    }
}

 

ComputeScreenPos
因为VS_POSITION在3188不起作用的原因,我辗转使用了很多不同的着色器语义(NORMAL, TEXCOORD_X,...)等用来保存 pos变量,但是均已失败告终,后来发现UnityCG.cginc 内部包含了 ComputeScreenPos函数,便尝试了一下,发现真的可行!其在vert阶段使用了TEXCOORD_X保存转换后的坐标,再在frag阶段讲变量还原。而我失败的原因是没有经过转换!至于为什么要转换,我估计是转换成采样uv的等同坐标,这样在vert 到 frag的时候数据才不会走样。还有ComputeScreenPos这个函数的分析,网上都有。

 

Shader "Custom/ScreenPos" {
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
    Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }
        
        Pass
        {
            Cull Off
            Lighting Off
            ZWrite Off
            Fog { Mode Off }
            Offset -1, -1
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #include "UnityCG.cginc"
            
            #pragma vertex vert
            #pragma fragment frag
            
            struct appdata_t
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
    
            struct v2f
            {
                float4 pos : SV_POSITION;
                half2 uv : TEXCOORD0;
                float4 screenPos : TEXCOORD1;
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                o.uv = v.uv;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
                o.screenPos = ComputeScreenPos(o.pos);
                //o.screenPos.xy /= o.screenPos.w;
                
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                // checker value will be negative for 4x4 blocks of pixels
                // in a checkerboard pattern
             float2 screenPos = i.screenPos.xy;// / i.screenPos.w;
              screenPos.xy *= _ScreenParams.xy;
              screenPos.xy = floor(screenPos.xy) *0.5;// 0.25;
                float checker = -frac(screenPos.x + screenPos.y); 

                // clip HLSL instruction stops rendering a pixel if value is negative
                //clip(checker);
                
                if(checker < -0.25)
                    return fixed4(0,0,0,0);

                // for pixels that were kept, read the texture and output it
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
                
            }
            ENDCG
        }
    }
}

 

简单说就是 ComputeScreenPos 是可以兼容低端显卡的做法(我用的3188),使用ComputeScreenPos在PC上效果还可以,但是在3188上效果不好,等于没用。但是总算是找出了解决方案。

 

参考:
https://blog.csdn.net/lt136022740/article/details/52859545
https://www.cnblogs.com/tekkaman/p/9606319.html
https://blog.csdn.net/linuxheik/article/details/86691117

 


unity3d, shader中  把一个定点惊醒mpv变换后,得到的一个float4的这个值,其实还并没有进行透视除法,需要我们手动的去除以最后一个w分量,这也是在计算屏幕坐标(ComputeScreenPos这个函数,定义在unitycg.cginc中)的时候,我最开始理解的一个误区)
————————————————
版权声明:本文为CSDN博主「lt136022740」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lt136022740/java/article/details/52859545


 

 

现在一目了然了,ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值,其范围为[0, w]。那么为什么Unity要这么做呢?Unity的本意是希望你把该坐标值用作tex2Dproj指令的参数值,tex2Dproj会在对纹理采样前除以w分量。当然你也可以像下面代码那样自己除以w分量后进行采样,但是效率不如内置指令tex2Dproj:

  1. pos = UnityObjectToClipPos(v.vertex);
  2. screenPos = ComputeScreenPos(o.pos);
  3. tex2D(_ScreenTex, float2(screenPos.xy / screenPos.w))

posted on 2020-05-18 15:54  GodZza  阅读(412)  评论(0编辑  收藏  举报