Game Jam 48h小DEMO(2.5D&3D)

使用的引擎为unity,管线为默认渲染管线

涉及世界观的设计,该demo主要为2.5D和3D,场景大部分为2.5D纸片,只有人物以及部分场景物为3D模型。因此此案例中除了人物需要考虑到光照情况外,其他的并无考虑到光照和阴影情况。场景虽然有6个,但打包的速度也是挺快的。

效果预览

 着色器

主要需要实现的材质所需着色器主要分为外描边、广告牌、天空球、透明着色器、不透明着色器这五个板块。但因为大多网上都有,所以记录一些感觉比较有意思的。

外描边

外描边用的是将模型背面的顶点偏移,从而实现描边效果。

Shader "Unlit/outLine"
{
    //外描边
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _OutLineCol("外描边颜色", Color) = (0.0, 0.0, 0.0, 0.0)
        _OutLine("外描边宽度", float) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        //描边pass
        Pass       {
           //剔除正面
            Cull Front
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            //参数
            uniform fiexed4 _OutLineCol;
            uniform float _OutLine;
            
            struct appdata
            {
                float4 vertex : POSITION;
                float4 normal : NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
            

            v2f vert (appdata v)
            {
                v2f o;
                //模型空间下的顶点坐标
                float3 posM = v.vertex;
                //扩展顶点
                posM = posM * (1 + _OutLine);
                //齐次裁剪空间下的顶点坐标
                o.vertex = UnityObjectToClipPos(posM);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 Linecol = _OutLineCol.rgb;
                return fixed4(Linecol, 1.0);
            }
            ENDCG
        }
        
        //mainTex Pass
        Pass
        {
            //剔除背面
            // Cull Back
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            //参数
            uniform sampler2D _MainTex;
            uniform float4 _MainTex_ST;
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

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

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

广告牌

用来实现场景中的植物方向可以跟随镜头的转动而发生转动,让其正面始终面向摄像机,让场景看起来可以更加立体一点。

 

Shader "Unlit/Billboard"
{
    Properties
     {
          _MainTex ("Texture", 2D) = "white" {}
           _VerticalBillboring("角度改变",Range(-50, 0)) = 2
      }
     SubShader
     {
         //对顶点进行变换需禁用批处理
         Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "true" "DisableBatching" = "True" }
         LOD 100
      
         Pass
         {
             //透明度混合
             ZWrite off
             Blend SrcAlpha OneMinusSrcAlpha
             Cull off
 
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
 
            #include "UnityCG.cginc"
 
             sampler2D _MainTex;
             float4 _MainTex_ST;
             float _VerticalBillboring;

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

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



            v2f vert (appdata v)
          {
              v2f o;
                //计算模型空间中的视线方向
              float3 objViewDir = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));

               //计算旋转矩阵的各个基向量
              float3 normalDir =normalize(objViewDir);
              normalDir.y = normalDir.y * _VerticalBillboring;
              normalDir = normalize(normalDir);
              float3 upDir =abs(normalDir.y) > 0.999 ? float3(0, 0, 1): float3(0, 1, 0);;
              float3 rightDir = normalize(cross(normalDir, upDir));//归一化
              upDir = normalize(cross(normalDir, rightDir));

                //用旋转矩阵对顶点进行偏移
              float3 localPos =rightDir * v.vertex.x + upDir * v.vertex.y + normalDir * v.vertex.z;

                //将偏移之后的值作为新的顶点传递计算
              o.vertex = UnityObjectToClipPos(float4(localPos,1));
              o.uv = TRANSFORM_TEX(v.uv, _MainTex);
              return o;
           }
 
             fixed4 frag (v2f i) : SV_Target
            {
                 fixed4 col = tex2D(_MainTex, i.uv);
                 return col;
             }
             ENDCG
         }
 }
  fallback "Transparent/VertexLit"
}

LOD分级着色器

在这里添加了可以通过脚本修改shaderLOD从而实现贴图颜色的转变的效果。

然后里面有点坑的点在于LOD大于等于400才有操作空间,低于400,unity会自动显示最高的LOD效果。

Shader "Unlit/changableShaderLOD"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    
    SubShader
    {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
        //shader 一定要控制在大于等于400才有操作空间
        LOD 400
        ZWrite Off
        Cull off
        Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
            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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

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

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
    
    SubShader
    {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 100
        ZWrite Off
        Cull off
        Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
            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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

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

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                fixed finalCol = col.b;
                return fixed4(finalCol, finalCol, finalCol, col.a);
            }
            ENDCG
        }
    }
}

TIPS

这个小DEMO中,最坑的点在于,因为都是使用面片作为场景的主要成分,包括树木、草丛和作为挡板的山脉,但是因为队列为透明列队(因为有些树丛需要透明混合,我把山脉和树丛那些都用了同一个着色器),从而所设置的ZWrite为Off,即其深度无法写入。因此在渲染的过程中,如果不修改渲染顺序,很容易发生渲染错乱。

所需要修改的是把山脉的Render Queue设置为Transparent-1,这样可以保证山脉永远都在树丛后面,不会发生重叠现象。

脚本

控制shaderLOD转变,通过点击物体,从而实现。

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

public class ShaderLODtest : MonoBehaviour
{
    //public int lod;
    public Shader shader;
    public GameObject target;
    public GameObject shining;

    void Start()
    {
          shader.maximumLOD = 200; //设置全局Lod, 比它小的Shader能够渲染
         // shader = gameObject.GetComponent<MeshRenderer>().material.shader;
          Debug.Log(this.shader.maximumLOD);
          shining.SetActive(false);
    }

    void Update()
    {

        if (Input.GetMouseButtonDown(0))
            //判断鼠标点击,触屏判断使用Input.touchCount
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            //这是鼠标
            RaycastHit hitInfo;
            if (Physics.Raycast(ray, out hitInfo))
            {

                if (hitInfo.collider.gameObject.tag != null)
                {
                    Debug.Log("ClickObjict:" + hitInfo.collider.gameObject);
                    if (hitInfo.collider.gameObject.tag == "NPC")
                    {
                        Debug.Log("tag is NPC");
                        this.shader.maximumLOD = 400;
                        Debug.Log(this.shader.maximumLOD);
                        shining.SetActive(true);
                    }
                }
            }
        }
    }
}

针对于摄像机视角的操作

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

public class controlPlayer : MonoBehaviour
{
    //旋转最大角度
    public int yMinLimit = -20;
    public int yMaxLimit = 80;
    //旋转速度
    public float xSpeed = 250.0f;
    public float ySpeed = 120.0f;
    //旋转角度
    private float x = 0.0f;
    private float y = 0.0f;

    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            //Input.GetAxis("MouseX")获取鼠标移动的X轴的距离
            x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
            y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
            y = ClampAngle(y, yMinLimit, yMaxLimit);
            //欧拉角转化为四元数
            Quaternion rotation = Quaternion.Euler(x, y, 0);
            transform.rotation = rotation;
        }
        else if (Input.GetAxis("Mouse ScrollWheel") != 0)
        {
            //鼠标滚动滑轮 值就会变化
            if (Input.GetAxis("Mouse ScrollWheel") < 0)
            {
                //范围值限定
                if (Camera.main.fieldOfView <= 100)
                    Camera.main.fieldOfView += 2;
                if (Camera.main.orthographicSize <= 20)
                    Camera.main.orthographicSize += 0.5F;
            }
            if (Input.GetAxis("Mouse ScrollWheel") > 0)
            {
                //范围值限定
                if (Camera.main.fieldOfView > 2)
                    Camera.main.fieldOfView -= 2;
                if (Camera.main.orthographicSize >= 1)
                    Camera.main.orthographicSize -= 0.5F;
            }
        }
    }
    //角度范围值限定
    static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp(angle, min, max);
    }
}

猜你喜欢

转载自blog.csdn.net/TingQiaoQiao/article/details/129287057