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:
-
pos = UnityObjectToClipPos(v.vertex);
-
screenPos = ComputeScreenPos(o.pos);
-
tex2D(_ScreenTex, float2(screenPos.xy / screenPos.w))