如何在Unity中将表面着色器转换为顶点/片段代码?

3
我想知道如何将这个表面着色器代码转换为顶点/片段编译指示语法。
Shader "Custom/Diamond Opaque Test" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _SpecColor ("Specular Color", Color) = (0.5,0.5,0.5,1)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.078125
        _RimPower ("Rim Power", Range(0,8.0)) = 3.0
        _ReflectColor ("Reflection Color", Color) = (1,1,1,0.5)
        [NoScaleOffset] _Cube ("Cubemap", CUBE) = "" {}
    }

    SubShader {

        CGPROGRAM
        #pragma surface surf BlinnPhong

        struct Input {
            float3 worldRefl;
            float3 viewDir;
        };

        samplerCUBE _Cube;
        fixed4 _Color;
        fixed4 _ReflectColor;
        half _Shininess;
        half _RimPower;

        void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = _Color.rgb;
            o.Gloss = _Color.a;
            o.Specular = _Shininess;
            half rim = saturate(dot (normalize(IN.viewDir), o.Normal));
            o.Emission = texCUBE (_Cube, IN.worldRefl).rgb * _ReflectColor.rgb * pow(rim,_RimPower);;
        }
        ENDCG
    } 
    FallBack "Mobile/Diffuse"
}

上面的着色器代码是下图左侧的红宝石: no shadow 我的目标是让它看起来像右边的宝石。在此过程中,我遇到了一些挑战。左侧的宝石是不透明的并且接受阴影。右侧的宝石具有正面透明度,但背面没有。然而,它不接受阴影。我必须将左侧的宝石设置为BlinnPhong,以便它可以投射和接收阴影。 shadow 是否可能为前后面的透明度创建不同的控件?透明对象是否有一种方法可以接受阴影?我想给宝石添加轮廓,以便可以从更远的地方看到它。是否可以在同一Pass中添加轮廓,还是必须有自己单独的Pass?此外,如何使接收到的阴影变暗?所有这些需求会使GPU负担过重吗?该游戏是为移动平台设计的。
我是CG编程的完全初学者。我的第一步是弄清如何将表面编程转换为顶点/片段。任何示例都将非常感激。
我在资产商店购买了几个宝石资产,但其中一些可以投射阴影但不能接收或完全不能投射或接收。这是怎么回事?右侧的宝石着色器来自于资产商店中购买的一个资产。我不想在未经他们允许的情况下在此网站上分享他们的着色器脚本。

我有点困惑,你的问题中有很多问题吗?1-表面到片段 2-双面透明 - Seyed Morteza Kamali
为什么你想将这个着色器转换成片段着色器?使用表面着色器更好,因为它更容易编辑。 - Seyed Morteza Kamali
1). 我购买的资产中有一些我喜欢的功能,但是它们是用顶点/片段编写的。我想取出代码的某些部分并添加到我的代码中。我想将表面转换为顶点/片段,以便我可以理解如何操作它。2). 我想通过控制正面和背面的alpha值来控制宝石中的雾量。设置整个宝石的alpha值会使其看起来更像玻璃或塑料。例如,我想将白色宝石的背面alpha值设置得更接近实心而不是较暗的颜色。希望这样说得清楚。 - user1105430
编写片段着色器并不容易,但如果你喜欢这样做,你应该学习片段着色器。无论如何,在每个光照建模中都有相应的片段着色器此处 - Seyed Morteza Kamali
1个回答

1
这实际上是一个比较复杂的过程,我建议您从Unity官网下载着手。大部分阴影处理在UnityStandardBRDF.cginc、UnityPBSLighting.cginc和UnityStandardCore.cginc中完成。如果您搜索,也可以在GitHub上找到这些文件。

基本上,前向添加着色器的基本结构如下:

Shader "MyForwardAddShader"
{
    Properties
    {
        _MainTex("Some texture", 2D) = "white"{}
    }

    SubShader
    {
        Tags {"RenderType"="Geometry" "Queue" = "Geometry"}

        Pass 
        {
            // Forward Base pass - this applies main directional light and ambient
            Name "FORWARD"
            Tags { "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma target 3.5

            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog
            #pragma multi_compile_instancing

            #pragma vertex vert
            #pragma fragment frag

            // I Like to keep these in separate include files
            #include "MyForwardBase.cginc"

            ENDCG
        }

        Pass
        {
            // Forward Add pass - this is added once per extra light source

            Name "FORWARD_DELTA"
            Tags { "LightMode" = "ForwardAdd" }
            Blend SrcAlpha One
            Fog { Color (0,0,0,0) } // in additive pass fog should be black
            ZWrite Off
            ZTest LEqual

            CGPROGRAM
            #pragma target 3.5

            #pragma multi_compile_fwdadd_fullshadows
            #pragma multi_compile_fog

            #pragma vertex vert
            #pragma fragment frag

            #include "MyForwardAdd.cginc"

            ENDCG
        }
    }
}

请注意,如果要投射阴影,您还需要一个shadowcaster pass(您可以直接复制标准着色器源代码中使用的pass),如果要使用烘焙光照,则还需要Meta pass。
像“MyForwardBase.cginc”这样的文件应包括顶点输入和输出结构的定义,以及用于通过的顶点和片段函数。在此设置中,您还需要在其中定义uniforms(属性变量)。一些可能需要用于光计算的变量:
// Calculate this in the vertex shader and normalize per fragment
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 viewDir = _WorldSpaceCameraPos.xyz - o.worldPos;

// In the ForwardBase pass, _WorldSPaceLightPos0.xyz stores the direction of the main directional light, instead of position. 
// Use this in the vertex function:
half3 mainLightColor = _LightColor0.rgb;
half3 mainLightDir = _WorldSpaceLightPos0.xyz;

对于ForwardBase的通行证,您需要包含“AutoLight.cginc”并在顶点函数中使用此宏:
COMPUTE_LIGHT_COORDS(o);

这就要求您在VertexOutput结构中拥有这个宏:

UNITY_LIGHTING_COORDS(6,7) // 6, 7 can be any texture interpolator IDs, they will translate to TEXCOORD6 and TEXCOORD7 in this case

前向添加光衰减的工作原理如下:

UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos.xyz); // Here, worldPos is stored in the vertex output struct and calculated per vertex
half3 lightColor = _LightColor0.rgb * atten;

还有一些宏用于查找像素是否处于阴影中,你可以在AutoLight.cginc中找到它们。在ForwardAdd通道中,光线方向是根据每个顶点计算的:

o.lightDir      = _WorldSpaceLightPos0.xyz - o.worldPos.xyz * _WorldSpaceLightPos0.w;

个人而言,我喜欢为每个功能创建小的CGIncludes,例如反射、折射、三平面映射等,并在需要时进行包含。我还倾向于将名为“Lighting”的单独函数保存在其自己的include文件中,因为光照计算在各个通道之间是共享的。该函数将所有变量(如lightDir、viewDir、albedo、roughness等)作为参数,并返回片段的阴影颜色。

就像我说的那样,这是一个相当复杂的过程,这只是一个入门指南。实现照明函数本身就是一件大事,但查看UnityStandardBRDF.cginc可以帮助解决此问题。我建议您从基本的Blinn-Phong模型开始调试,并从那里改进。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接