深入URP之Shader篇6: SimpleLit Shader分析(2) Vertex Shader

这篇具有很好参考价值的文章主要介绍了深入URP之Shader篇6: SimpleLit Shader分析(2) Vertex Shader。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Simpe Lit Forward Pass

Vertex Shader 函数

看看在顶点shader中都计算了什么

  • 计算顶点坐标
    这个和之前一样:
    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
    再复习一下,这个函数位于ShaderVariablesFunctions.hlsl中。
  • 计算法线和切线
    VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
    输入的是ObjectSpace(OS)的法线和切线,这个是Unity在导入时处理好的数据。具体计算如下:
VertexNormalInputs GetVertexNormalInputs(float3 normalOS, float4 tangentOS)
{
    VertexNormalInputs tbn;

    // mikkts space compliant. only normalize when extracting normal at frag.
    real sign = tangentOS.w * GetOddNegativeScale();
    tbn.normalWS = TransformObjectToWorldNormal(normalOS);
    tbn.tangentWS = TransformObjectToWorldDir(tangentOS.xyz);
    tbn.bitangentWS = cross(tbn.normalWS, tbn.tangentWS) * sign;
    return tbn;
}

其中,GetOddNegativeScale函数位于SPR Core的SpaceTransforms.hlsl中:

real GetOddNegativeScale()
{
    // FIXME: We should be able to just return unity_WorldTransformParams.w, but it is not
    // properly set at the moment, when doing ray-tracing; once this has been fixed in cpp,
    // we can revert back to the former implementation.
    return unity_WorldTransformParams.w >= 0.0 ? 1.0 : -1.0;
}

这里面的unity_WorldTransformParams却又位于URP的UnityInput.hlsl中:

real4 unity_WorldTransformParams; // w is usually 1.0, or -1.0 for odd-negative scale transforms

回到GetVertexNormalInputs中,首先计算world space的法线和切线没啥问题,只不过变换法线需要用逆转置矩阵因此使用一个特定的函数TransformObjectToWorldNormal,而变换切线使用普通的变换向量的函数TransformObjectToWorldDir即可,这两个函数自己写shader也经常用。而计算副切线是使用法线和切线的叉积,但是其结果需要校正符号。而这个符号是Object space切线的w和GetOddNegativeScale返回值的乘积。
这个我暂时没弄清楚,按照之前掌握的知识,Object space切线的w的正负,是由DCC工具计算切线时的手向性决定的,而unity_WorldTransformParams的w按照unity的注释和奇数次的负缩放有关,这个很模糊。先留着等搞清楚再修改。
当然如果只是学习怎么用,怎么在unity shader中计算切线,就照着这个来就行。

  • 顶点光照
    half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
    调用VertexLighting函数,输入世界空间的位置和法线,计算逐顶点的光照颜色。
    VertexLighting函数在Lighting.hlsl中:
half3 VertexLighting(float3 positionWS, half3 normalWS)
{
    half3 vertexLightColor = half3(0.0, 0.0, 0.0);

#ifdef _ADDITIONAL_LIGHTS_VERTEX
    uint lightsCount = GetAdditionalLightsCount();
    for (uint lightIndex = 0u; lightIndex < lightsCount; ++lightIndex)
    {
        Light light = GetAdditionalLight(lightIndex, positionWS);
        half3 lightColor = light.color * light.distanceAttenuation;
        vertexLightColor += LightingLambert(lightColor, light.direction, normalWS);
    }
#endif

    return vertexLightColor;
}

可见,必须激活关键字_ADDITIONAL_LIGHTS_VERTEX,才会计算附加光的顶点光照。或依次获取所有的附加光,得到其颜色强度,然后使用LightingLambert函数计算出一个漫反射光照颜色:

half3 LightingLambert(half3 lightColor, half3 lightDir, half3 normal)
{
    half NdotL = saturate(dot(normal, lightDir));
    return lightColor * NdotL;
}

所以,顶点光照计算的就是附加光的漫反射lambert颜色,所有附加光都计算然后叠加到一起。

  • 雾的参数
    half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
    这个函数之前Unlit的时候看过,但是没多做解释。其实就是简单的深度雾,根据当前坐标的z值在clip和far之间的比例(统一到[0,1]之间)使用线性插值或者指数函数计算雾的颜色值。关于z值的计算回头统一有一篇集中分析。
  • 法线贴图的参数
#ifdef _NORMALMAP
    output.normal = half4(normalInput.normalWS, viewDirWS.x);
    output.tangent = half4(normalInput.tangentWS, viewDirWS.y);
    output.bitangent = half4(normalInput.bitangentWS, viewDirWS.z);
#else
    output.normal = NormalizeNormalPerVertex(normalInput.normalWS);
    output.viewDir = viewDirWS;
#endif

如果使用法线贴图,则会输出世界空间的法线,切线,和副切线。并且会把世界空间的视线方向夹带在这3个向量的w中,节省了一个输出向量(因为这些向量是varying,需要GPU去插值的,越少越好,而Vector4的插值和Vector3是一样的消耗,SIMD的原因)。如果不使用法线贴图,那么只要输出世界空间的法线和视线方向即可。注意这儿的法线使用了一个NormalizeNormalPerVertex函数处理:

// A word on normalization of normals:
// For better quality normals should be normalized before and after
// interpolation.
// 1) In vertex, skinning or blend shapes might vary significantly the lenght of normal.
// 2) In fragment, because even outputting unit-length normals interpolation can make it non-unit.
// 3) In fragment when using normal map, because mikktspace sets up non orthonormal basis.
// However we will try to balance performance vs quality here as also let users configure that as
// shader quality tiers.
// Low Quality Tier: Normalize either per-vertex or per-pixel depending if normalmap is sampled.
// Medium Quality Tier: Always normalize per-vertex. Normalize per-pixel only if using normal map
// High Quality Tier: Normalize in both vertex and pixel shaders.
real3 NormalizeNormalPerVertex(real3 normalWS)
{
    #if defined(SHADER_QUALITY_LOW) && defined(_NORMALMAP)
        return normalWS;
    #else
        return normalize(normalWS);
    #endif
}

这个函数会根据shader quality以及是否使用法线贴图来决定是否要归一化法线,具体见上面的注释。

  • 输出lightmap UV和SH
OUTPUT_LIGHTMAP_UV(input.lightmapUV, unity_LightmapST, output.lightmapUV);
OUTPUT_SH(output.normal.xyz, output.vertexSH);

这两个OUTPUT是根据是否启用lightmap定义的宏,如下:

#ifdef LIGHTMAP_ON
    #define DECLARE_LIGHTMAP_OR_SH(lmName, shName, index) float2 lmName : TEXCOORD##index
    #define OUTPUT_LIGHTMAP_UV(lightmapUV, lightmapScaleOffset, OUT) OUT.xy = lightmapUV.xy * lightmapScaleOffset.xy + lightmapScaleOffset.zw;
    #define OUTPUT_SH(normalWS, OUT)
#else
    #define DECLARE_LIGHTMAP_OR_SH(lmName, shName, index) half3 shName : TEXCOORD##index
    #define OUTPUT_LIGHTMAP_UV(lightmapUV, lightmapScaleOffset, OUT)
    #define OUTPUT_SH(normalWS, OUT) OUT.xyz = SampleSHVertex(normalWS)
#endif

如果启用了lightmap,就会对lightmap UV进行变换并输出到output.lightmapUV中,否则啥也不干。对于球谐系数SH则是相反,使用lightmap就啥也不干,否则就会采样该顶点的球谐系数:

// Samples SH L0, L1 and L2 terms
half3 SampleSH(half3 normalWS)
{
    // LPPV is not supported in Ligthweight Pipeline
    real4 SHCoefficients[7];
    SHCoefficients[0] = unity_SHAr;
    SHCoefficients[1] = unity_SHAg;
    SHCoefficients[2] = unity_SHAb;
    SHCoefficients[3] = unity_SHBr;
    SHCoefficients[4] = unity_SHBg;
    SHCoefficients[5] = unity_SHBb;
    SHCoefficients[6] = unity_SHC;

    return max(half3(0, 0, 0), SampleSH9(SHCoefficients, normalWS));
}

// SH Vertex Evaluation. Depending on target SH sampling might be
// done completely per vertex or mixed with L2 term per vertex and L0, L1
// per pixel. See SampleSHPixel
half3 SampleSHVertex(half3 normalWS)
{
#if defined(EVALUATE_SH_VERTEX)
    return SampleSH(normalWS);
#elif defined(EVALUATE_SH_MIXED)
    // no max since this is only L2 contribution
    return SHEvalLinearL2(normalWS, unity_SHBr, unity_SHBg, unity_SHBb, unity_SHC);
#endif

    // Fully per-pixel. Nothing to compute.
    return half3(0.0, 0.0, 0.0);
}

SH系数是通过light probe烘焙出来的低频球谐光照信息,使用SH函数可以用极小的代价去存储光照信息。具体也许会单独讲一篇。

  • 输出雾参数和顶点光照
    output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
    将计算出来的雾参数和顶点光照颜色合并到一个向量中输出。
  • 在顶点上计算阴影坐标
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    output.shadowCoord = GetShadowCoord(vertexInput);
#endif

必须是启用关键字REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR,这样就会在顶点级别计算阴影坐标然后varying插值,这样显然精度较低,但是会提高效率,具体什么情况会这样用后面再看。另外关于阴影肯定是要单独一篇或几篇分析的,所以这儿就不深入了。

本篇小结

本篇分析了SimplLit Forward pass的 Vertex Shader 函数LitPassVertexSimple,这个函数基本上就是为光照提供各种参数,只有附加光的漫反射有可能在这儿计算。这个函数考虑了有无NormalMap, LightMap以及顶点光照等各种情况。下篇我们就看一下这个simple lit到底在fragment shader中是怎么计算的。文章来源地址https://www.toymoban.com/news/detail-430606.html

到了这里,关于深入URP之Shader篇6: SimpleLit Shader分析(2) Vertex Shader的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity中URP下的SimpleLit顶点着色器

    在上一篇文章中,我们了解了URP下SimpleLit的整体框架。 Unity中URP下 SimpleLit框架 我们在这篇文章中,来了解一下URP下SimpleLit中,光照核心部分的顶点着色器干了什么,方便之后我们自定义自己的光照Shader。 因为被Unity封装的原因。所以,顶点着色器 和 片元着色器 主要是在这

    2024年01月17日
    浏览(34)
  • Unity中URP下的SimpleLit片元着色器

    在上篇文章中,我们了解了Unity中URP下SimpleLit中的顶点着色器。 Unity中URP下的SimpleLit顶点着色器 我们在这篇文章中,来了解一下Unity中URP下SimpleLit中的片元着色器。有助于我们之后写自己的光照Shader。 这里传入参数为 顶点着色器输出的Varyings结构体 返回结果用 out修饰来代替

    2024年01月20日
    浏览(27)
  • Unity中URP下的SimpleLit的 Lambert漫反射计算

    在之前的文章中,我们已经知道了 SimpleLit 下的主光数据怎么获取。 Unity中URP下获取主灯信息 Unity中ShaderGraph下获取主灯 有了这些数据,我们就可以计算 Lambert漫反射 和 BlinnPhone高光反射 了。 我们在获取了主光信息后 Light mainLight = GetMainLight1(inputData, shadowMask, aoFactor); 就来到了

    2024年01月21日
    浏览(83)
  • Unity中Shader URP 简介

    在这篇文章中,我们主要介绍一下Unity中的URP(通用渲染管线)是什么 Unity帮助文档 Windows and UWP Mac and IOS Android Xbox One PlayStation4 Nintendo Switch WebGL All current VR platforms 在Unity2018以前,Unity使用的一直都是Build-in Render Pipeline(内置渲染管线) 在早期使用内置渲染管线时,Unity为了适配

    2024年01月16日
    浏览(37)
  • Unity 性能优化之Shader分析处理函数ShaderUtil.HasProceduralInstancing: 深入解析与实用案例

    点击封面跳转到Unity国际版下载页面 在Unity中,性能优化是游戏开发过程中非常重要的一环。其中,Shader的优化对于游戏的性能提升起着至关重要的作用。本文将深入解析Unity中的Shader处理函数 ShaderUtil.HasProceduralInstancing ,并提供一些实用案例来展示其用法。 ShaderUtil.HasProce

    2024年02月09日
    浏览(31)
  • Unity Shader从内置渲染管线迁移到URP

    Unity 在URP中将shader更新为了HLSL语言,使用build-in shader 无法直接在URP中使用 这里讲一下关于shader的更新方法 参考 From Built-in to URP Tags 添加 \\\"RenderPipeline\\\" = \\\"UniversalPipeline\\\" CGPROGRAM ENDCG 改变为 HLSLPROGRAM ENDHLSL #include \\\"UnityCG.cginc\\\" 更改为 #include \\\"Packages/com.unity.render-pipelines.universal/Sh

    2024年02月05日
    浏览(32)
  • Unity URP Shader(HLSL)踩坑日记(一)

    最近开始转TA,刚开始学习,资料比较杂乱,其中遇到的问题和一些计算方式,记录一下,后续会一直完善补充。 注意此时Properties中的属性,如果要开启SRP合批,需要放到CBUFFER代码块中。 应用阶段准备的数据---- 顶点着色处理数据(返回值为处理后的数据)---- ------ 片元着色器

    2024年01月17日
    浏览(36)
  • Unity Spine 3.8 (URP) 踩坑(Shader报错修改)

    今天搜索spine优化,看到一篇文章项目导入多个Spine动画 合批 降低DrawCall -- UWA问答 | 游戏开发者互动问答社区 | 侑虎科技 提供了新思路,打算尝试一下URP。但美术使用的spine版本是3.8,项目用的Unity2021.3.11f1c2,直接导入spine URP包,shader报错:half4 不能转成 SurfaceData2D;遂改下

    2024年02月12日
    浏览(32)
  • Unity URP Shader “Redefinition of _Time“ error

    强烈建议先尝试阅读本文之后自行解决:https://zhuanlan.zhihu.com/p/360566324 我这里记录一下我的思路: 首先检查URP升级是否正确,主要看Asset是否设置,ShaderGraph表现是否正常 尝试排除是否是未定义宏导致的问题,主要是对比ShaderGraph自动生成的代码 确认自己的代码是否使用了

    2024年02月15日
    浏览(29)
  • unity URP 利用particle system制作简单的shader交互

    首先这里制作了一个简单交互,使用shader grapgh,根据计算距离和变化数值的差实现交互后的扩散,同时计算消散遮罩让它逐渐谈去。 将他赋予材质物体,根据脚本传入位置和逐渐变化的大小后,呈现这样的效果。 但是,shader graph这样的工具,在做这种效果非常快的同时,也

    2024年02月07日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包