线性空间下,如何处理GrabTexture显示过亮的情况?

线性空间下,如何处理GrabTexture显示过亮的情况?

本期我们聚集了这几个话题:安卓平台线性空间下GrabTexture显示过亮、Graphics API的选择、导入动画时discarding joint scale/translation...


这是第124篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)


渲染

Q1:我们的扭曲效果使用了GrabPass,在PC平台和iOS都正常,但是Android平台在不开Bloom的情况下会显示错误,本来是一个很透明的面片,变成了透明度比较低的白片,调试整个Shader之后怀疑是颜色空间的问题,切换回Gamma空间后显示正常。求教下有人遇到过这个问题么,附Shader代码。

Shader "Custom/Effect/Distortion" {
    Properties {
        _TintColor ("Tint Color", Color) = (1.0, 1.0, 1.0, 1.0)
        _Refraction ("Refraction", 2D) = "white" {}
        _RefractionIntensity ("Refraction Intensity", Range(0, 1)) = 1
    }
    SubShader {
        Tags {
            "IgnoreProjector"="True"
            "Queue"="Transparent"
            "RenderType"="Transparent"
        }

        GrabPass{ "_GrabTexture" }
        Pass {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off
            Lighting Off
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma target 3.0
            uniform sampler2D _GrabTexture;
            uniform sampler2D _Refraction; uniform float4 _Refraction_ST;
            uniform float _RefractionIntensity;
            fixed4 _TintColor;
            struct VertexInput {
                float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
                float4 vertexColor : COLOR;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                float4 vertexColor : COLOR;
                float4 grabPos : TEXCOORD1;
            };
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.uv0 = TRANSFORM_TEX(v.texcoord0, _Refraction);
                o.vertexColor = v.vertexColor;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.grabPos = ComputeGrabScreenPos(o.pos);
                return o;
            }
            fixed4 frag(VertexOutput i) : COLOR {
                float4 _Refraction_var = tex2D(_Refraction, i.uv0);
                float2 sceneUVs = (i.grabPos.xy/i.grabPos.w + (_Refraction_var.rg*i.vertexColor.a*_RefractionIntensity));
                return tex2D(_GrabTexture, sceneUVs);
            }
            ENDCG
        }
    }
}

附注: frag最后强制转线性空间后接近正常return pow(tex2D(_GrabTexture,sceneUVs),2.2)

根据题主提供的代码,我们在红米4X上复现了该问题。的确如题主所预判,跟机型有关,加上pow(color, 2.2)之后看起来似乎正常了。
查阅了相关的官方文档,看到有类似的Bug反馈,这里显示了GLCore修了,估计GLES没有修。
https://issuetracker.unity3d.com/issues/grabpass-matrial-with-grabpass-shader-renders-much-brighter-on-opengl-in-linear
同时,我们又在Unity 2018版本上用红米4X做了测试,发现该问题已解决。建议题主可以尝试用Unity 2018试试。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5b6bc0f8339d267d357c6d30


OpenGL

Q2:关于Unity中Graphics API的选择,我曾在UWA问答上看过相关的帖子,但还是有几个相关的疑问:
请输入图片描述
1)像Android,选择里面只有ES2.0和3.0选项,那像上图那样把两者都选了,和勾选Auto有什么区别呢?是不是都是优先ES3.0,然后不行的话就退到ES2.0?
2)康来有说ES2.0的效果会比3.0的效果差一些,我自己改的Unity PBR Shader也遇到2.0的效果比3.0的效果差的情况,抓帧发现是手机上实际的Shader编译版本不一样,想问下同样的Shader(假设都走同样的分支,不因为不同宏定义走不一样的代码),两者编译出来还会有这么大的表现差异吗?
3)关于效率上,Graphics API的选择是只影响GPU方面的效率,不会影响CPU上的效率吧?

学渲染的小彩笔过来抛个破砖引块美玉。
第一个问题,题主的理解是正确的,都是优先ES3.0 然后2.0的。
针对题主的第二个问题:ES3.0引入了新版本的GLSL Shading Language,所以编出来的东西肯定不一样。
另外ES2.0和ES3.0不仅代表了API的变化,也代表了硬件能力的提升。别的不说,ES3.0完全支持了32位浮点数运算,光是这个就足够带来效果上的明显差异。另外,ES2.0要求Depth Buffer至少16Bits,而ES3.0要求支持24Bits和32Bits。
关于第三个问题,因为API的变换,CPU的效率必然会变化,例如VAO,ES3.0支持而ES2.0不支持。

参考文献:
https://www.khronos.org/registry/OpenGL/specs/es/2.0/es_cm_spec_2.0.pdf
https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf
http://www.openglsuperbible.com/2013/12/09/vertex-array-performance

感谢凯奥斯@UWA问答社区提供回答,欢迎大家转至社区进一步交流:
https://answer.uwa4d.com/question/5b6e3d581e88b37d34e652a1


资源管理

Q3:如何生成被4整除的NPOT图集?如何选择一个好的图集划分策略?我们纹理压缩格式选择的是ETC2(Android) + ASTC(iOS),所以只需要图片尺寸被4整除就行了。

现在图集用的是UGUI自带的SpritePacker,生成的图集尺寸默认是POT,会导致部分图集塞不满,空白区域较多,而美术人工调整图集归属是很麻烦的事,所以希望让它生成NPOT+被4整除的图集,这样图集归属可以随便弄,空白都不会很多。但看了下自定义图集打包策略的文档,似乎没有相关的接口开放。

TexturePacker也只能提供被2整除的NPOT图集,所以问问有没有同学实现过类似的系统,给个思路。

另外,再问几个问题:
1)项目中存在不同纹理压缩格式会导致(较大的)性能损失吗? 比如“全部ETC2"和“尽量优先ETC1,不行再ETC2”这两种做法。
2)NPOT现代的实现(比如OpenGLES的NPOT扩展),应该不是自动扩展到POT这种方式吧? 如果是这种方式,NPOT的内存占用就是个坑。
3)NPOT的性能和POT比如何? 能确定的是POT肯定不会比NPOT差,但两者的性能差距Google有一大堆不同的说法。所以这里的问题是在现代(移动)GPU上,需要考虑NPOT和POT的性能差距问题吗?
4)我们之前倾向于照顾美术的习惯,可以的话尽量少约束,所以图集划分上希望做到“美术完全不知情不参与”,这样就会带来图集塞不满的情况。图集划分这一块大家通常是怎么做的?怎样在“图集划分完美”和“开发\维护成本高昂”之间找一个比较好的平衡?我们主要考虑的还是减少工作成本:别花太多时间在这个上面,然后能保证一个还不错的效果就行,不需要完美。

说一下我们项目的图集策略,仅供参考:
1)只允许方图;
2)保证2的n次幂(解决整除4的问题);
3)格式同楼主,但是不同意ETC1和2混用,明显ETC2压缩质量好,速度快;
4)保证ICON和图片的名称确定和规范化,美术不用关心图集,由策划来规划并进行配表,即使图集有改动,只需要修改配表的图集名称即可;
5)图集的规划分为公共和系统图集,但是相关的UI界面都会有图集的引用索引列表,以此来限制美术及策划的使用范围;
6)做项目是一个三方制约的环境,给太多的自由,势必会增加工作和沟通成本,对美术设计进行系统规范的设计是必不可少的。
感谢郑骁@UWA问答社区提供回答

我们目前也是ETC(Android) + ASTC(iOS),但是要求还是POT的,只是开放了非方形的贴图尺寸用于合并的二方连续贴图。UI部分是要求UI使用TexturePacker合并之后的Atlas提交上来,必须是POT的。
关于题主具体的问题,TexturePacker貌似是有编写插件的方法?不知道是否可以通过插件限定导出尺寸是4的倍数,来满足你的需求。
照顾美术的习惯是一个好程序的表现,给题主点赞,但是要注意这部分照顾的成本不要转嫁到性能或者其他职位的身上,否则不如教一下美术,让美术按照规则来制作。
感谢贾伟昊@UWA问答社区提供回答

关于NPOT,可以参考官方文档:
https://docs.unity3d.com/Manual/ImportingTextures.html
关于补充的第二点:
https://answer.uwa4d.com/question/5b4422bbf91024518ab77aed
另外楼主非要用NPOT的图集,当然也没人拦得住你,下面有一个JS写的Pack算法:
https://github.com/jakesgordon/bin-packing
感谢凯奥斯@UWA问答社区提供回答

该问答来自UWA问答社区,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5b67bea7339d267d357c6cd8


动画

Q4:我使用Mecanim将模型导入到Unity时,提示“ has translation animation that will be discarded“以及”has sacle animation that will be dscarded”,即某些关节的Scale/Translate动画被丢弃,但是有一部分动画却能正常播放。所以,Mecanim到底是否支持Scale/Translate动画呢?

Mecanim的Humanoid模式是不支持Scale/Translate动画的,如果Scale/Translate动画是必要的,可以切换到Generic模式。另外,部分关节的Scale/Translate动画能正常工作,应该是因为这些关节是非Humanoid关联的(以Generic模式运行的关节)。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5b71086c1e88b37d34e652b4


逻辑代码

Q5:PropertyInfo的CanWrite = false,如果这个属性是个List,只能读不能写,要怎么给这个List赋值呢?PropertyInfo.SetValue不行,因为这个属性不能写入,现在想通过List的Add方法添加值,不知道怎么做好,希望大家能给些建议。

题主可以尝试用PropertyInfo.GetValue传入对象,返回值用as关键字转换成List,接着调用Add。(PropertyInfo.GetValue(obj) as List).Add(xxx)。不过这样可能会有问题:如果这个List是每次new出来的,那么依然无法修改对象内部变量。

感谢凯奥斯@UWA问答社区提供回答,欢迎大家转至社区进一步交流:
https://answer.uwa4d.com/question/5b6a47591e88b37d34e65258

今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
官方技术QQ群:793972859(原群已满员)