首页 > 其他分享 >Unity 低功耗玉石

Unity 低功耗玉石

时间:2023-11-22 18:34:30浏览次数:40  
标签:VSOutput 低功耗 玉石 float half3 Unity normalW 透射 float3

前言

曾使用过UE5的substrate系统基于BSDF实现过玉石材质,效果雀氏nice但消耗太高了!因此本篇基于Unity介绍如何模拟透射来实现一个低功耗的玉石材质
效果如下

本篇同步发布于http://chenglixue.top/index.php/unity/90/

总体框架

模拟透射光

  • 思路

    • 因为透射现象是一种光打在物体发生散射,其中一部分光进入物体内部,且这一部分的光的某一部分成功穿过物体背面,最终我们便可以看到下图现象,所以很明显为了模拟这一现象,需要考虑相机方向向量 和 光源方向向量

    • 但仅仅考虑相机方向向量和光源方向向量还不够,因为光线发生散射时,进入物体内部的光线方向是随机的,所以需要想办法对原光线进行扰动

    • 最后还需考虑厚度对透射的影响

实现

模拟透射

  • 可以通过扰动法线方向来模拟散射光线

    // 模拟散射光线
    float3 scatterMainLightDirw = -normalize(mainLightDirW + normalW * _DistortMainLightDir);
    
  • 随后需要考虑相机方向向量 和 光源方向向量

    // 对透射物体的光线向量和视角向量进行dot
    float VoInvL = saturate(dot(viewDirW, scatterMainLightDirw));
    
    // pow控制透射对比度
    // _MainLightScatterIntensity控制透射强度
    VoInvL = pow(VoInvL, _Contrast) * _MainLightScatterIntensity;
    

    下图展示了透射的对比度

  • 计算透射颜色

    // 模型的厚度贴图.值越高,越厚,越不容易透射
    float thickness = SAMPLE_TEXTURE2D(_ThicknessMap, sampler_ThicknessMap, psInput.uv).r;
    
    // _ScatterColor.rgb控制透射颜色
    half3 scatterColor = mainLightColor * VoInvL * (1 - thickness) * _ScatterColor.rgb;
    

漫反射

  • 模拟的透射光并没有乘上diffuse纹理,若不添加diffuse计算会导致背对光源看向模型,模型是纯黑的。并添加一个_AddColor来手动调整玉石颜色

    half4 diffuseTex = SAMPLE_TEXTURE2D(_DiffuseMap, sampler_DiffuseMap, psInput.uv) * _DiffuseTint;
    
    // half lambert
    float NoL = saturate(dot(normalW, mainLightDirW) * 0.5 + 0.5);
    
    half3 diffuseColor = diffuseTex * mainLightColor * NoL;
    
    diffuseColor += _AddColor.rgb;  // 手动添加的额外颜色
    

环境光反射

  • 因为是反射,所以可以添加fresnel效果进行丰富

    TEXTURECUBE(_CubeMap);                  SAMPLER(sampler_CubeMap);       float4 _CubeMap_HDR;
    
    float Fresnel(float VoN, float Power)
    {
        return pow(1.0 - saturate(VoN), Power);
    }
    
    float3 fresnel = Fresnel(VoN, _FresnelExp);
    // 环境光反射
    half4 cubemapColor = SAMPLE_TEXTURECUBE(_CubeMap, sampler_CubeMap, reflectDirW);
    // 解码HDR
    half3 environmentColor = DecodeHDREnvironment(cubemapColor, _CubeMap_HDR) * fresnel * _Exposure;
    
    

模拟天光

  • 这里模拟的天光也就是一个类似AO的效果

    half3 skyLightColor = saturate(dot(normalW, float3(0, 1, 0) * 0.5 + 0.5)) * diffuseTex * _SkyLightOpacity;
    

多光源

  • 这里只考虑额外光源的透射效果即可(这样美术效果更为合适)

    [KeywordEnum(OFF, ON)]_ADDITION_LIGHT("Addition Light ?", Int) = 1
    
    #pragma shader_feature_local _ADDITION_LIGHT_ON _ADDITION_LIGHT_OFF
    
    #if defined _ADDITION_LIGHT_ON
    
    // 额外光源的数量
    int addLightCounts = GetAdditionalLightsCount();	
    for(int i = 0; i < addLightCounts; ++i)
    {
        Light addLight = GetAdditionalLight(i, psInput.positionW);
        float3 addLightDirW = normalize(addLight.direction);	// 额外光源的方向
        half3 addLightColor = addLight.color;	// 额外光源的颜色
    
        // 模拟散射光
        float3 scatterAddLightDirw = -normalize(addLightDirW + normalW * _DistortAddLightDir);
        float addLightVoInvL = saturate(dot(viewDirW, scatterAddLightDirw));
        addLightVoInvL = pow(addLightVoInvL, _Contrast) * _AddLightScatterIntensity;
        half3 addLightScatterColor = addLightColor * (1 - thickness) * addLightVoInvL * addLight.distanceAttenuation * addLight.shadowAttenuation;
    
        outputColor += addLightScatterColor;
    }
    #endif
    

Bloom + GT Tonemapping

  • 这个实现在之前的文章实现过,可以看这http://chenglixue.top/index.php/unity/73/

全部代码

  • Shader

    {
        Properties
        {
            [Header(Diffuse Setting)]
            [Space(3)]
            [MainTexture]_DiffuseMap("Diffuse Texture", 2D) = "white" {}
            [HDR][MainColor]_DiffuseTint("Diffuse Color Tint", Color) = (1, 1, 1, 1)
            [HDR]_AddColor("Additional Color", Color) = (1, 1, 1, 1)
            [Space(30)]
            
            [Header(Light Setting)]
            _DistortMainLightDir("Distort Main Light Direction", Range(0, 1)) = 0
            _DistortAddLightDir("Distort Add Light Direction", Range(0, 1)) = 0
            _Contrast("Contrast", Float) = 5
            _MainLightScatterIntensity("Main Light Scatter Intensity", Float) = 1
            _AddLightScatterIntensity("Addition Light Scatter Intensity", Float) = 1
            [HDR]_ScatterColor("Scatter Color", Color) = (1, 1, 1, 1)
            _SkyLightOpacity("SkyLight Opacity", Range(0, 1)) = 0
            [KeywordEnum(OFF, ON)]_ADDITION_LIGHT("Addition Light ?", Int) = 1
            [Space(30)]
            
            [Header(Thickness Setting)]
            _ThicknessMap("Thickness Tex", 2D) = "white" {}
            [Space(30)]
            
            [Header(Environment Setting)]
            _CubeMap("Cube map", Cube) = "white" {}
            _Exposure("Exposure", Range(0, 8)) = 1
            _Rotation("Rotation",Range(0,360)) = 0  
            _FresnelExp("Fresnel Exp", Range(1, 10)) = 5
        }
        
        SubShader
        {
            Tags
            {
                "Pipeline" = "UniversalPipeline"
                "RenderType" = "Opaque"
                "Queue" = "Geometry"
            }
            LOD 100
            
            HLSLINCLUDE
            
            ENDHLSL
    
            Pass
            {
                Tags
                {
                    "LightMode" = "UniversalForward"
                }
                
                HLSLPROGRAM
    
                #pragma shader_feature_local _ADDITION_LIGHT_ON _ADDITION_LIGHT_OFF
                #pragma multi_compile __ _MAIN_LIGHT_SHADOWS    // 计算阴影衰减
                #pragma multi_compile __ _MAIN_LIGHT_SHADOWS_CASCADE    // 得到正确的阴影坐标
                #pragma multi_compile __ _SHADOWS_SOFT  //计算软阴影
                #pragma multi_compile __ ADDITIONAL_LIGHT_CALCULATE_SHADOWS // 计算额外光的阴影衰减和距离衰减
                #pragma multi_compile __ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS   //计算阴影投射
                
                #include_with_pragmas "Assets/Jade/Library/Jade.hlsl"
                
                #pragma vertex VS
                #pragma fragment PS
                ENDHLSL
            }
    
            Pass
            {
                Tags
                {
                    "LightMode" = "ShadowCaster"
                }
                
                HLSLPROGRAM
    
                #include_with_pragmas "Assets/Jade/Library/Jade.hlsl"
                
                #pragma vertex VSShadow
                #pragma fragment PSShadow
                
                ENDHLSL
                }   
            }
    }
    
  • HLSL

    #pragma once
    
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
    // 个人的工具库
    #include "Assets/Shader/MyUtil/MyUtil.hlsl"	
    
    // -------------------------------------------- variable definition --------------------------------------------
    CBUFFER_START(UnityPerMaterial)
    
    float4 _DiffuseMap_ST;
    half4 _DiffuseTint;
    half4 _AddColor;
    
    float _DistortMainLightDir;
    float _DistortAddLightDir;
    float _Contrast;
    float _MainLightScatterIntensity;
    float _AddLightScatterIntensity;
    half4 _ScatterColor;
    
    float _Exposure;
    float _Rotation;
    float _FresnelExp;
    
    float _SkyLightOpacity;
    
    // 获取主光源和其余光源的方向(Unity自动完成赋值)
    half3 _LightDirection;
    
    CBUFFER_END
    
    TEXTURE2D(_DiffuseMap);                 SAMPLER(sampler_DiffuseMap);
    TEXTURE2D(_ThicknessMap);               SAMPLER(sampler_ThicknessMap);
    TEXTURECUBE(_CubeMap);                  SAMPLER(sampler_CubeMap);       float4 _CubeMap_HDR;
    
    struct VSInput
    {
        float4 positionL : POSITION;
        float3 normalL : NORMAL;
        float2 uv : TEXCOORD0;
    };
    
    struct PSInput
    {
        float4 positionH : SV_POSITION;
        float4 shadowUV : TEXCOORD3;
        
        float3 normalW : TEXCOORD0;
        float3 positionW : TEXCOORD1;
        
        float2 uv : TEXCOORD2;
    };
    
    // -------------------------------------------- function definition --------------------------------------------
    
    PSInput VS(VSInput vsInput)
    {
        PSInput vsOutput;
    
        // Get pos
        VertexPositionInputs vertexPosInput = GetVertexPositionInputs(vsInput.positionL);
        vsOutput.positionH = vertexPosInput.positionCS;
        vsOutput.positionW = vertexPosInput.positionWS;
    
        // Get normal
        VertexNormalInputs vertexNormalInput = GetVertexNormalInputs(vsInput.normalL);
        vsOutput.normalW = vertexNormalInput.normalWS;
    
        // Get uv
        vsOutput.uv = TRANSFORM_TEX(vsInput.uv, _DiffuseMap);
        vsOutput.shadowUV = TransformWorldToShadowCoord(vsOutput.positionW);
    
        return vsOutput;
    }
    
    half4 PS(PSInput psInput) : SV_TARGET
    {
        half3 outputColor = 0.f;
    
        // about texture
        half4 diffuseTex = SAMPLE_TEXTURE2D(_DiffuseMap, sampler_DiffuseMap, psInput.uv) * _DiffuseTint;
        float thickness = SAMPLE_TEXTURE2D(_ThicknessMap, sampler_ThicknessMap, psInput.uv).r;
            
        // about light
        Light mainLight = GetMainLight(psInput.shadowUV);
        float3 mainLightDirW = normalize(mainLight.direction);
        half3 mainLightColor = mainLight.color;
    
        // about direction
        float3 normalW = normalize(psInput.normalW);
        float3 viewDirW = normalize(GetCameraPositionWS() - psInput.positionW);
        // 模拟散射光线
        float3 scatterMainLightDirw = -normalize(mainLightDirW + normalW * _DistortMainLightDir);
        // 环境光反射
        float3 reflectDirW = normalize(reflect(-viewDirW, normalW));
        reflectDirW = RotatEnvironment(_Rotation, reflectDirW);
    
        float NoL = saturate(dot(normalW, mainLightDirW) * 0.5 + 0.5);
        float VoN = saturate(dot(viewDirW, normalW));
        float VoInvL = saturate(dot(viewDirW, scatterMainLightDirw));   // 模拟透射现象
        VoInvL = pow(VoInvL, _Contrast) * _MainLightScatterIntensity;
        float3 fresnel = Fresnel(VoN, _FresnelExp);
    
        // result color
        // diffuse
        half3 diffuseColor = diffuseTex * mainLightColor * NoL;
        diffuseColor += _AddColor.rgb;  // 手动添加的额外颜色
        // 模拟透射光
        half3 scatterColor = mainLightColor * VoInvL * (1 - thickness) * _ScatterColor.rgb;
        // 环境光反射
        half4 cubemapColor = SAMPLE_TEXTURECUBE(_CubeMap, sampler_CubeMap, reflectDirW);
        half3 environmentColor = DecodeHDREnvironment(cubemapColor, _CubeMap_HDR) * fresnel * _Exposure;
        // 模拟天光
        half3 skyLightColor = saturate(dot(normalW, float3(0, 1, 0) * 0.5 + 0.5)) * diffuseTex * _SkyLightOpacity;
    
        #if defined _ADDITION_LIGHT_ON
    
        int addLightCounts = GetAdditionalLightsCount();
        for(int i = 0; i < addLightCounts; ++i)
        {
            Light addLight = GetAdditionalLight(i, psInput.positionW, half4(1.f, 1.f, 1.f, 1.f));
            float3 addLightDirW = normalize(addLight.direction);
            half3 addLightColor = addLight.color;
    
            // 模拟散射光
            float3 scatterAddLightDirw = -normalize(addLightDirW + normalW * _DistortAddLightDir);
            float addLightVoInvL = saturate(dot(viewDirW, scatterAddLightDirw));
            addLightVoInvL = pow(addLightVoInvL, _Contrast) * _AddLightScatterIntensity;
            half3 addLightScatterColor = addLightColor * (1 - thickness) * addLightVoInvL * addLight.distanceAttenuation * addLight.shadowAttenuation;
    
            outputColor += addLightScatterColor;
        }
        #endif
    
        outputColor += diffuseColor + scatterColor + environmentColor + skyLightColor;
        
        return half4(outputColor, 1.f);
    }
    
    PSInput VSShadow(VSInput vsInput)
    {
        PSInput VSOutput;
    
        VSOutput.uv = TRANSFORM_TEX(vsInput.uv, _DiffuseMap);
    
        VSOutput.normalW = TransformObjectToWorldNormal(vsInput.normalL);
        VSOutput.positionW = TransformObjectToWorld(vsInput.positionL);
        Light mainLight = GetMainLight();
                    
        VSOutput.positionH = TransformWorldToHClip(ApplyShadowBias(VSOutput.positionW, VSOutput.normalW, _LightDirection));
                    
        #if UNITY_REVERSED_Z
        VSOutput.positionH.z = min(VSOutput.positionH.z, VSOutput.positionH.w * UNITY_NEAR_CLIP_VALUE);
        #else
        VSOutput.positionH.z = max(VSOutput.positionH.z, VSOutput.positionH.w * UNITY_NEAR_CLIP_VALUE);
        #endif
    
        return VSOutput;
    }
    
    half4 PSShadow(PSInput psInput) : SV_TARGET
    {
        return 0;
    }
    

reference

星云大神的RTR3

标签:VSOutput,低功耗,玉石,float,half3,Unity,normalW,透射,float3
From: https://www.cnblogs.com/chenglixue/p/17850013.html

相关文章

  • 【Unity】在游戏中实现虚拟摇杆功能
    使用场景在手机游戏开发中,使用虚拟摇杆控制角色进行移动。虚拟摇杆预制体制作在UI界面添加虚拟摇杆外圈图片在外圈下添加内圈图片将位置置于外圈中心位置添加脚本usingTools;usingUnityEngine;///<summary>///虚拟摇杆管理器///</summary>......
  • 【Unity】伪随机算法之PRD
    概念在游戏制作中通常会有暴击等概率性事件,有两种方法实现,一种就是正常使用随机算法实现,真随机受人品影响,对游戏体验极不友好,所以就提出了伪随机概念,常见的就是PRD算法。P(N)=C*NP为最终概率C为概率增量N为次数随着攻击次数增加概率增加,当暴击时将N重置为1,没有......
  • Unity AssetBundle Browser 使用
    https://blog.csdn.net/WenHuiJun_/article/details/113178688 参数说明BuildTarget-构建捆绑包的目标平台OutputPath-用于保存构建的捆绑包的路径。默认为AssetBundles/。可以手动编辑该路径,也可以选择“Browse”。要恢复默认命名约定,请点击“Reset”。ClearF......
  • Unity Asset Bundle Browser 导入
    https://github.com/Unity-Technologies/AssetBundles-Browser 有时候使用packManager来下载,但会下载不下来,超时无法连接服务器将上面链接内容下载下来后直接丢到工程Asset下随便目录就行,然后回报错,Boo什么错的,无脑删除掉Tests测试目录就行了 最后在Unity菜单栏[Windo......
  • Unity InputField 输入框
    参数text:输入框中显示的文本内容。placeholder:输入框中未输入内容时显示的占位符文本。characterLimit:输入框中可输入的最大数字符限制。contentType:输入框中可输入的内容类型,如整数、小数、密码等。onValueChanged:输入框内容改变时触发的事件。 函数用常A......
  • Unity中欧拉角
    什么是欧拉角?(1)使用单个角度来保存方位(2)X与Z沿自身坐标系旋转,Y沿世界坐标旋转(3)API:Vector3eulerAngle=this.tranform.rulerAngles;优点:(1)仅使用三个数字表达方位,占用空间小(2)沿坐标轴旋转的单位维角度,符合人的思考方式(3)任意三个数字都是合法的,不存在不合法的欧拉角缺点:(1)方......
  • Unity 按键监听
    voidUpdate(){if(Input.GetKeyDown(KeyCode.UpArrow)){TestText.text="上";}}Input.GetKeyDown(keycode.space)这是监听空格键按下,具体看keycode的枚举对应不同按键Input.GetKeyUp是判断某个按键抬起Input.GetKey是判断某个按......
  • 大话Unity Shader,带你带你从0开始了解
    前言Shader,一款中文名为着色器的东西,对于很多开发者来言它是一个神秘的存在,想学但是又不知道如何去学,或者学了一段时间又发现Get不到它的点,始终感觉游离在外,不得其法。那么本文今天就来给大家讲解一下如何从零基础入门Shader,目的在于让初学Shader的程序员或者美术er可以快速进入......
  • Unity 卡通渲染之角色篇
    前言作为新博客的第一篇,就用卡渲作为开篇叭!毕竟是个二次元乐。本篇同步发表于http://chenglixue.top/index.php/unity/73/之前使用UE的后处理做过简单的卡渲,但因其灵活性很差,很多操作都需涉及到更改管线,且奈何本人在校用的笔记本,一次build就得好久,因此放弃对卡渲的深入。如今对......
  • Unity学习笔记--数据持久化XML文件(1)
    XML相关Xml是可拓展标记语言,一种文件格式。我们使用xml来完成对数据持久化的存储。等待我们有一程序运行结束之后,将内存中的数据进行保存,(保存在硬盘/服务器)实现对数据的持久化存储。xml文件的读取和保存以及修改要点:XMl文件的加载XML文件节点的查找访问XML文件节点内......