zoukankan      html  css  js  c++  java
  • 崩坏3角色渲染分析

    https://blog.csdn.net/liumazi/article/details/78858811

    渲染逆向方法:

    用Adreno Profiler抓帧, 并且分析其中的OpenGL调用及相关资源(顶点数据、纹理、Shader代码等)

    其中Shader代码和在Unity里选中Shader并点击Compile and show code看到的gles3部分类似, 建议转换成更易于阅读的形式

    卡通着色思路:

    光照计算仍然是漫反射+高光反射, 其中漫反射是HalfLambert, 高光反射公式来自于Blinn-Phong模型

    比较关键的点是, 漫反射部分不是渐变的, 而是分了3档, 以实现卡通风格的层次感, 即所谓的梯度漫反射(Ramped Diffuse)

    上图是还原出来的Unity Shader及渲染结果, 下面简要介绍一下该Shader的实现细节..

    Main Color:

    主纹理, rgb为基础颜色, a的作用见下文

    Bloom Mask:

    未使用

    Light Map:

    光照纹理, rgb作用见下文

    mainColor.a:

    主纹理alpha通道, 表示不受光照影响的程度, 为1.0时无论是否有光照影响都为原颜色, 为0.0时仅包含光照计算结果,

    如身体和头发主纹理中, 一些黄色的部分, alpha值较高, 用来保持高亮

    从主纹理中分离出来的alpha通道, 见下图

    Avatar_Kiana_C2_Texture_Body_Color_RGB2048_A.png和Avatar_Kiana_C2_Texture_Hair_Color_Common_A.png

    以及 输出-主纹理-A通道.png

    UsingBloomMask:

    材质属性, 是否使用BloomMask纹理调整不受光照影响的程度, 默认关闭

    公式为 mainColor.a *= tex2D(_BloomMaskTex, i.texcoord5.xy).x

    注意: 只能减弱或保持原状, 因为纹理采样结果在0.0~1.0之间

    UsingDitherAlpha:

    材质属性, 是否应用alpha抖动, 默认关闭

    FirstShadowMultColor:

    材质属性, 暗面亮度一, xyz分量分别对应主纹理rgb通道, 值越小越暗, 此处为(0.72941, 0.6, 0.65098)

    公式为fristShadowColor = mainColor.rgb * _FirstShadowMultColor.xyz

    SecondShadowMultColor:

    材质属性, 暗面亮度二, xyz分量分别对应主纹理rgb通道, 值越小越暗, 此处为(0.65098, 0.45098, 0.549019)

    公式为secondShadowColor = mainColor.rgb * _SecondShadowMultColor.xyz

    i.color.r和lightMapColor.g和LightArea和SecondShadow:

    顶点颜色r通道和光照纹理g通道, 两者乘积rgProduct用来做暗面颜色选择, 公式如下

    diffuseColor = rgProduct >= 0.090000033 ? otherColor : shadowColor

    otherColor = ((rgProductFix + i.halfLambert) * 0.5 >= _LightArea) ? mainColor.rgb : fristShadowColor

    shadowColor = ((rgProduct + i.halfLambert) * 0.5 >= _SecondShadow) ? fristShadowColor : secondShadowColor

    其中,

    rgProductFix是rgProduct经过微调的一个值;

    i.halfLambert是半兰伯特值, 即法线与光线夹角的余弦值映射到0.0~1.0范围内, 夹角越小值越接近1, 用于表示漫反射部分的强度;

    _LightArea和_SecondShadow是材质属性, 分别为用于选择 原颜色和暗面一、暗面一和暗面二的阈值;

    上述公式可理解为,

    如果rgProduct非常小的话, 在暗面一和暗面二中选一个; 否则, 在原颜色和暗面一中选一个,

    相当于漫反射率不再是根据法线渐变, 而是分了3档(按阈值选择), 以此实现卡通风格的层次感,

    更进一步地, 当rgProduct非常小的时候, 除非i.halfLambert比较大, 否则选的往往是暗面二

    下图1 输出-明暗分布.png, 其中不同颜色代表了不同的区域, 绿色和黑色是暗面一和暗面二, 白色和红色是原颜色和暗面一

    下图2 输出-顶点R通道×光照纹理G通道.png, 其中比较黑的区域正对应了上图的绿色和黑色区域

     

    Shininess:

    材质属性, 光泽度, Blinn-Phong光照模型中用于计算高光反射的指数, 用于控制高光区域的亮点大小, 值越大亮点半径越小

    lightMapColor.b:

    光照纹理b通道, 用于使某些区域更容易出现高光, 因为出现高光的条件是 lightMapColor.b + blinnSpec > 1.0

    其中blinnSpec是按照Blinn-Phong光照模型计算出来的高光度, 越大表示高光反射越强,

    下图 ID_118_B.png, 这是头发的光照纹理b通道, 比较明显; 以及 ID_113_B.png和 输出-光照纹理-B通道.png

    lightMapColor.r和LightSpecColor和SpecMulti:

    lightMapColor.r为光照纹理r通道, LightSpecColor和SpecMulti为材质属性, 共同控制当存在高光时高光的颜色,

    公式为 _LightSpecColor.xyz * _SpecMulti * lightMapColor.r

    见下图 ID_113_R.png、ID_118_R.png、输出-光照纹理-R通道.png

    最终产生的高光分布: (比想象中弱很多)

    Color:

    材质属性, 用于调整光照计算结果, 只能调的更弱或者保持原状,

    公式为litColor.xyz = (diffuseColor + specColor) * _Color.xyz;

    其他输出图:

    输出-主纹理-RGB通道.png 和 输出-顶点色-RGB通道.png

     

    皮肤shader:

    与上述shader类似, 但是没有BloomMask, 增加了ProbToggle用于调节肤色(未见实际使用), 并且mainColor.a不再有上述作用

    描边的实现:

    将模型在观察空间中, 按法线往外扩张, 以剔除正面的方式绘制 (Shader中声明的顶点属性是切线, 但实际提交的数据应该还是法线)

    下图 输出-膨胀方向.png, 其中红色表示右下, 绿色表示左上, 黄色表示右上, 黑色表示左下, 注意: 只是大概的方向不是绝对的

    最终描边结果 输出-最终结果.png, 为了更明显, 描边颜色设置为了黑色, 游戏中实际的值为(0.4117647, 0.3112941, 0.3768184)

  • 相关阅读:
    String、StringBuffer与StringBuilder的区别
    案例2:用一条SQL查询出数学语文成绩都大于80分的学生姓名?
    案例1:写一个压缩字符串的方法,例如aaaabbcxxx,则输出a4b2c1x3。
    jsp的九大内置对象及作用
    SQL语句总结2018-11-7
    kafka-spark streaming (一)
    python while嵌套循环
    docker-compose.yml样例(mysql主从+mycat读写分离)
    docker-compose管理daocker
    docker搭建私有registry
  • 原文地址:https://www.cnblogs.com/alps/p/9318278.html
Copyright © 2011-2022 走看看