当前位置: 首页 > article >正文

【Unity3D】实现Decal贴花效果,模拟战旗游戏地形效果

目录

一、基础版

二、Post Process 辉光Bloom效果 + 矩形渐隐


涉及知识点:Decal贴花、屏幕后处理Bloom、屏幕空间构建世界空间、ChracterController物体移动、Terrain地形创建

一、基础版

Unity 2019.4.0f1 普通渲染管线(非URP、非HDRP)
URP HDRP 在Unity 2021.2以上均可直接使用内置的贴花系统,具体网上有说明。

贴花物体是一个个Cube立方体,我将它拉长了Y轴 便于能接触到地面。

其中角色物体胶囊体要进行设置渲染层级,GetComponent<MeshRenderer>().material.renderQueue = 3001; //将人物放到贴花物体之后渲染,不被贴花物体影响

如这个Cube长方体就是红色边缘的Cube贴花,贴花颜色是红色。

贴花物体是透明层,不写入深度和深度检测,以及裁剪正面,不裁剪背面。

可参考:贴花(Decal)效果在Shader里是如何实现的_哔哩哔哩_bilibili

核心代码是DepthToWorldPosition,屏幕空间反向构建世界空间,很类似全局雾效,注意摄像机要开启深度贴图。摄像机挂上这个如下脚本设置:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class OpenDepthTexture : MonoBehaviour
{
    public DepthTextureMode mode;
    private void Awake()
    {
        GetComponent<Camera>().depthTextureMode = mode;
    }
}

Shader "Unlit/SimpleDecal"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        LOD 100

        Pass
        {
            //Blend One DstAlpha
            Blend SrcAlpha OneMinusSrcAlpha
            //Blend One One
            ZWrite Off
            ZTest Off
            Cull Front 
            
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 screenPos : TEXCOORD3;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _CameraDepthTexture;
            fixed4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.screenPos = ComputeScreenPos(o.vertex);
                return o;
            }

            float3 DepthToWorldPosition(float4 screenPos)
            {
                float depth = Linear01Depth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture,screenPos)));//线性深度值
                float4 ndcPos = (screenPos/screenPos.w) * 2 - 1;  //除以w是归一化Unity提供的屏幕坐标[0,1]  *2-1操作 转[-1,1]得到ndc(仅有x,y有效位)
                float3 clipPos = float3(ndcPos.x, ndcPos.y, 1) * _ProjectionParams.z;  //ndc补z位,直接用1 远平面值,之后乘以far远平面距离得到裁剪坐标
                //clipPos还缺一个w,实际z值就是w值故完整clipPos是(clipPos.xyzz) 再直接转视图空间得到远平面的视图空间坐标点(即Depth为1的点)                
                float3 viewPos = mul(unity_CameraInvProjection, clipPos.xyzz).xyz * depth; //*depth相当于一个Lerp操作,depth是[0,1]范围的值,取到depth深度的视图点
                float3 worldPos = mul(UNITY_MATRIX_I_V,float4(viewPos, 1)).xyz; //视图转世界坐标
                return worldPos;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldPos = DepthToWorldPosition(i.screenPos);
                float4 localPos = mul(unity_WorldToObject, float4(worldPos, 1.0));
                clip(float3(0.5,0.5,0.5) - abs(localPos.xyz));

                fixed2 decalUV = fixed2(localPos.x, localPos.z);
                decalUV = decalUV + 0.5; //模型空间 [-0.5,0.5] => [0,1] uv空间
                fixed4 color = tex2D(_MainTex, decalUV) * _Color;
                return color;
            }
            ENDCG
        }
    }
}

玩家胶囊体脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player3D : MonoBehaviour
{
    public GameObject greenPrefab;
    public GameObject redPrefab;
    public int size = 5;

    public Terrain terrain;

    private CharacterController characterController;
    private float h;
    private float v;
    private float verticalVelocity;
    public float moveSpeed = 1;


    // Start is called before the first frame update
    void Start()
    {
        Vector3 pos = transform.position;
        int halfSize = Mathf.FloorToInt(size / 2);
        for (int i = -halfSize; i <= halfSize; i++)
        {
            for (int j = -halfSize; j <= halfSize; j++)
            {
                int v = Mathf.Abs(i) + Mathf.Abs(j);
                if (v <= halfSize)
                {
                    GameObject go = GameObject.Instantiate(v == halfSize ? redPrefab : greenPrefab, transform);
                    go.transform.position = new Vector3(pos.x + i, pos.y, pos.z + j);
                }
            }
        }
        characterController = GetComponent<CharacterController>();

        GetComponent<MeshRenderer>().material.renderQueue = 3001; //将人物放到贴花物体之后渲染,不被贴花物体影响
    }



    private void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        Vector3 moveDir = transform.forward * v + transform.right * h;

        if (!characterController.isGrounded)
        {
            verticalVelocity -= 9.8f * Time.deltaTime;
            moveDir.y = verticalVelocity;
        }
        else
        {
            verticalVelocity = -0.5f;
        }

        characterController.Move(moveDir * moveSpeed * Time.deltaTime);
    }
}

二、Post Process 辉光Bloom效果 + 矩形渐隐

Package Manager导入Post Processing


需确保摄像机开启HDR,查看相应的Graphics Settings设置

如果你的是安卓环境,那么就要去看安卓下的HDR是否开启,如果你是URP、HDRP,则是看对应管线资源的配置是否有开启HDR。

摄像机挂上后处理组件 Post-process Layer,并设置摄像机和Layer要勾选我们想要辉光效果的层级,例如自定义的HDR层

创建一个PostProcess空物体,添加如下图组件Post-process Volume,并且设置它的Layer为HDR

点击组件的New按钮会创建一个Profile资源,再点击Add effect... 添加Unity -> Bloom曝光效果

设置Intensity强度适当5~10左右
Threshold阈值[0,1]范围保持默认即可(如果你设置大于1会导致曝光效果几乎没有)
Soft Knee 软曝光?可以自定义
Diffusion 漫反射,当曝光的地形在有漫反射的物体上时会叠加漫反射的强度好像,反正我不需要拉到最左边即可(即1)其他请自行研究,如下图即可得到gif图效果,如果没有辉光说明Layer层级设置不对,所有需要辉光的物体都要设置Layer层级是HDR(由Post-process Layer指定的层级)

贴花Shader脚本修改如下:

Shader "Unlit/SimpleDecal"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        [HDR]
        _Color ("Color", Color) = (1,1,1,1)
        _Range ("Range", Range(0,0.5)) = 0.45
        _FadeIntensity ("Fade Intensity", float) = 2
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        LOD 100

        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off
            ZTest Off
            Cull Front
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 screenPos : TEXCOORD3;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _CameraDepthTexture;
            fixed4 _Color;
            float _Range;
            float _FadeIntensity;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.screenPos = ComputeScreenPos(o.vertex);
                return o;
            }

            float3 DepthToWorldPosition(float4 screenPos)
            {
                float depth = Linear01Depth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture,screenPos)));//线性深度值
                float4 ndcPos = (screenPos/screenPos.w) * 2 - 1;  //除以w是归一化Unity提供的屏幕坐标[0,1]  *2-1操作 转[-1,1]得到ndc(仅有x,y有效位)
                float3 clipPos = float3(ndcPos.x, ndcPos.y, 1) * _ProjectionParams.z;  //ndc补z位,直接用1 远平面值,之后乘以far远平面距离得到裁剪坐标
                //clipPos还缺一个w,实际z值就是w值故完整clipPos是(clipPos.xyzz) 再直接转视图空间得到远平面的视图空间坐标点(即Depth为1的点)                
                float3 viewPos = mul(unity_CameraInvProjection, clipPos.xyzz).xyz * depth; //*depth相当于一个Lerp操作,depth是[0,1]范围的值,取到depth深度的视图点
                float3 worldPos = mul(UNITY_MATRIX_I_V,float4(viewPos, 1)).xyz; //视图转世界坐标
                return worldPos;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldPos = DepthToWorldPosition(i.screenPos);
                float4 localPos = mul(unity_WorldToObject, float4(worldPos, 1.0));
                clip(float3(0.5,0.5,0.5) - abs(localPos.xyz));

                fixed2 decalUV = fixed2(localPos.x, localPos.z);
                decalUV = decalUV + 0.5; //模型空间 [-0.5,0.5] => [0,1] uv空间
                fixed4 color = tex2D(_MainTex, decalUV) * _Color;

                //由外到内渐隐 (圆形渐隐)
                //float2 center = float2(0.5, 0.5);
                //float alpha = lerp(0, distance(float2(0,0), center), distance(decalUV, center));
                //color.a *= alpha;

                //由外到内渐隐(矩形渐隐)
                fixed2 uv = decalUV - 0.5; //转[-0.5,0.5]空间 方便计算
                float x = abs(uv.x) - _Range;
                float y = abs(uv.y) - _Range;               
                //float sum = x+y ;//max(0, x) + max(0, y);
                float sum = max(x, y);
                //color.a *= smoothstep(0, (0.5 - _Range) * 2, sum);
                color.a *= smoothstep(0, (0.5 - _Range) * _FadeIntensity, sum);
                return color;
            }
            ENDCG
        }
    }
}

有些bug就是Scene场景看不到贴花地形了,暂时没研究处来是啥情况


http://www.kler.cn/a/522997.html

相关文章:

  • vulfocus/thinkphp:6.0.12 命令执行
  • mysql DDL可重入讨论
  • QT+mysql+python 效果:
  • 记录 | 基于Docker Desktop的MaxKB安装
  • 【教学类-89-02】20250128新年篇02——姓名藏头对联(星火讯飞+Python,五言对联,有横批)
  • Node.js基础
  • 基于物联网设计的疫苗冷链物流监测系统
  • 【反悔堆】【hard】力扣630. 课程表 III
  • 基于多智能体强化学习的车联网通信中时间敏感网络的路由和调度模型
  • 【漫话机器学习系列】066.贪心算法(Greedy Algorithms)
  • 物业巡更系统在现代社区管理中的优势与应用探讨
  • 深入解析 Linux 内核内存管理核心:mm/memory.c
  • 【C++】设计模式详解:单例模式
  • 青少年编程与数学 02-007 PostgreSQL数据库应用 18课题、性能监控
  • 穿心莲内酯(andrographolide)生物合成CYP72-文献精读106
  • Go:基于Go实现一个压测工具
  • neo4j-community-5.26.0 install in window10
  • 学习数据结构(3)顺序表
  • 简易CPU设计入门:控制总线的剩余信号(四)
  • 原生 Node 开发 Web 服务器
  • 一个基于Python+Appium的手机自动化项目~~
  • 【面试】【前端】【性能优化】前端性能优化总结
  • 用XAMPP安装PHP环境(Window系统)
  • [c语言日寄]越界访问:意外的死循环
  • 网络仿真工具Core环境搭建
  • 2025年AI手机集中上市,三星Galaxy S25系列上市