음영 효과의 실현 방법

전언


Unity 엔진에 포함된 섀도우 기능은 보다 효과적인 섀도우맵입니다.본고는 프로젝터를 사용하여 음영을 생성하는 또 다른 실시 방식을 소개했다.다음은 실행 중인 프레젠테이션 영상입니다.여기를 클릭하면 HD 영상이 표시됩니다.

1. 기능의 실현


1. 주광원의 그림자 투영을 닫는다



위의 그림에서 보듯이 그림자를 투영할 때는 주 광원을 끄고 그림자를 만들어야 합니다.

2. 프로젝터 설정


다음 그림에서 보듯이 Projector 어셈블리를 추가한 후 Projector의 GameObject 방향을 조정합니다.


3. 핵심 코드 설명


위에서 설명한 대로 Projector Shadow 스크립트를 만듭니다.

3.1 먼저 렌더링 작성

// render textureを作成する
        mShadowRT = new RenderTexture(mRenderTexSize, mRenderTexSize, 0, RenderTextureFormat.R8);
        mShadowRT.name = "ShadowRT";
        mShadowRT.antiAliasing = 1;   // アンチエイリアシング(antialiasing)をオフにする
        mShadowRT.filterMode = FilterMode.Bilinear;
        mShadowRT.wrapMode = TextureWrapMode.Clamp;     // wrapmodeをClampに設定する必要がある
우선, 이 RenderTexture의 형식은 R8이며, 이 형식에 따라 제작된 무늬 기억 장치의 사용량이 가장 적다는 것을 주의하십시오.
런타임 시 텍스쳐가 표시됩니다.

2048×2048 텍스쳐를 생성하려면 메모리가 4MB에 불과합니다.그런 다음 AntiAliasing이 1로 설정됩니다.즉, 혼합에 저항하여 열리지 않는다는 것이다.WrapMode를 Clamp로 설정합니다.
최종 실행 매개변수는 다음 그림과 같습니다.그림의 DepthBuffer에 대해 코드는 설정되지 않았지만 기본값은 OFF입니다.DepthBuffer를 사용할 필요가 없으므로 투영 섀도우로 작성된 렌더기는 닫아야 합니다.

3.2 Projector 설정

//projector初期化
        mProjector = GetComponent<Projector>();
        mProjector.orthographic = true;
        mProjector.orthographicSize = mProjectorSize;
        mProjector.ignoreLayers = mLayerIgnoreReceiver;
        mProjector.material.SetTexture("_ShadowTex", mShadowRT);
여기서 중요한 것은 프로젝터를 정투영으로 설정하는 것이다.또한 다음 그림에서 보듯이 프로젝터의 크기를 설정하고 프로젝터의 무시층을 설정합니다.

프로젝터의 크기는 23으로 설정되고 레이어는 Unit으로 무시됩니다.즉 게임에서 제작된 모든 단원이다.

3.3 프로젝터 Camera 만들기

//camera初期化
        mShadowCam = gameObject.AddComponent<Camera>();
        mShadowCam.clearFlags = CameraClearFlags.Color;
        mShadowCam.backgroundColor = Color.black;
        mShadowCam.orthographic = true;
        mShadowCam.orthographicSize = mProjectorSize;
        mShadowCam.depth = -100.0f;
        mShadowCam.nearClipPlane = mProjector.nearClipPlane;
        mShadowCam.farClipPlane = mProjector.farClipPlane;
        mShadowCam.targetTexture = mShadowRT;
생성된 Camera의 ClearFlags를 투명 색상으로 설정합니다.
Camera의 투명한 색상의 Background Color를 검정색으로 설정합니다.
Camera도 정투영이기 때문에 정투영의 사이즈는 프로젝트or의 사이즈와 같아야 합니다.
Camera의 Depth를 -100으로 설정합니다.이것은 메인 카메라보다 더 빨리 표현한다는 것을 의미한다.
Camera의 nearClipPlane과 nearClipPlane은 프로젝터의 설정과 같다.
Camera의 TargetTexture는 작성된 RenderTexture로 설정됩니다.즉, 카메라는 모든 객체를 이 렌더링으로 렌더링합니다.

3.4 렌더링 방법의 선택


이 절이 중점이다.몇 편의 문장을 참고하여 마지막으로 두 가지 방법을 총결하였다.그 중에서 CommandBuffer의 방법은 실제 프로젝트에 적합하고 렌더링 효율을 높일 수 있다고 생각합니다.
먼저 코드의 설치를 보십시오.
private void SwitchCommandBuffer()
    {
        Shader replaceshader = Shader.Find("ProjectorShadow/ShadowCaster");

        if (!mUseCommandBuf)
        {
            mShadowCam.cullingMask = mLayerCaster;

            mShadowCam.SetReplacementShader(replaceshader, "RenderType");
        }
        else
        {
            mShadowCam.cullingMask = 0;

            mShadowCam.RemoveAllCommandBuffers();
            if (mCommandBuf != null)
            {
                mCommandBuf.Dispose();
                mCommandBuf = null;
            }

            mCommandBuf = new CommandBuffer();
            mShadowCam.AddCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, mCommandBuf);

            if (mReplaceMat == null)
            {
                mReplaceMat = new Material(replaceshader);
                mReplaceMat.hideFlags = HideFlags.HideAndDontSave;
            }
        }
    }

3.4.1 CommandBuffer를 사용하지 않을 경우 코드는 다음 두 줄입니다.
mShadowCam.cullingMask = mLayerCaster;

mShadowCam.SetReplacementShader(replaceshader, "RenderType");
Camera 렌더링 레이어의 GameObject를 설정하고 Camera 렌더링을 교체할 Shader를 설정합니다.
다음 그림과 같이 Camera는 작성된 모든 Unit만 렌더링합니다.

Camera에서 사용하는 Shader의 경우 일반적인 정점/세션 Shader로 처리할 수 있습니다.
Shader "ProjectorShadow/ShadowCaster"
{
    Properties
    {
        _ShadowColor("Main Color", COLOR) = (1, 1, 1, 1)
    }

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

        Pass
        {
            ZWrite Off
            Cull Off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            struct v2f
            {
                float4 pos : POSITION;
            };

            v2f vert(float4 vertex:POSITION)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                return o;
            }

            float4 frag(v2f i) :SV_TARGET
            {
                return 1;
            }

            ENDCG
        }
    }
}
이 농담기는 쓰기 깊이를 닫고 자르지 않고 흰색을 출력합니다.
3.4.2 CommandBuffer를 사용할 때 코드는 다음과 같습니다.
mShadowCam.cullingMask = 0;

            mShadowCam.RemoveAllCommandBuffers();
            if (mCommandBuf != null)
            {
                mCommandBuf.Dispose();
                mCommandBuf = null;
            }

            mCommandBuf = new CommandBuffer();
            mShadowCam.AddCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, mCommandBuf);

            if (mReplaceMat == null)
            {
                mReplaceMat = new Material(replaceshader);
                mReplaceMat.hideFlags = HideFlags.HideAndDontSave;
            }
Camera의 CullingMask는 0으로 설정되어 있습니다.즉, Camera는 객체를 렌더링하지 않으며 모든 렌더링은 CommandBuffer에서 수행합니다.그런 다음 Camera의 CommandBuffer 목록에 CommandBuffer를 생성합니다.
CommandBuffer 렌더링에 필요한 Material 작성Material에서 사용해야 하는 Shader는 위의 Projector Shadow/Shadow Caster입니다.
프레임을 업데이트하는 경우:
```
private void FillCommandBuffer()
{
mCommandBuf.Clear();
    Plane[] camfrustum = GeometryUtility.CalculateFrustumPlanes(mShadowCam);

    List<GameObject> listgo = UnitManager.Instance.UnitList;
    foreach (var go in listgo)
    {
        if (go == null)
            continue;

        Collider collider = go.GetComponentInChildren<Collider>();
        if (collider == null)
            continue;

        bool bound = GeometryUtility.TestPlanesAABB(camfrustum, collider.bounds);
        if (!bound)
            continue;

        Renderer[] renderlist = go.GetComponentsInChildren<Renderer>();
        if (renderlist.Length <= 0)
            continue;

        // 可視的なrenderがあるかどうか
        // あるならGameObjectの全体がレンダリングされます
        bool hasvis = false;
        foreach (var render in renderlist)
        {
            if (render == null)
                continue;

            RenderVis rendervis = render.GetComponent<RenderVis>();
            if (rendervis == null)
                continue;

            if (rendervis.IsVisible)
            {
                hasvis = true;
                break;
            }
        }

        foreach(var render in renderlist)
        {
            if (render == null)
                continue;

            mCommandBuf.DrawRenderer(render, mReplaceMat);
        }           
    }
}
게임에서 만든 모든 단원을 훑어보십시오.먼저 테이퍼를 관리한 다음 투영 카메라에 표시되지 않는 유닛을 관리합니다.주로 아래 두 줄의 코드입니다.
Plane[] camfrustum = GeometryUtility.CalculateFrustumPlanes(mShadowCam);

bool bound = GeometryUtility.TestPlanesAABB(camfrustum, collider.bounds);
투영 카메라의 원추체를 계산하고 함수를 사용하여 단원의 Collider가 원추 안에 있는지 판단한다.이렇게 하면 현재 프레임에서 볼 수 있는 유닛을 제외할 수 있습니다.
다음은 다음과 같은 판단을 한다.
```
Renderer[] renderlist = go.GetComponentsInChildren();
if (renderlist.Length <= 0)
continue;
        // 可視的なrenderがあるかどうか
        // あるならGameObjectの全体がレンダリングされます
        bool hasvis = false;
        foreach (var render in renderlist)
        {
            if (render == null)
                continue;

            RenderVis rendervis = render.GetComponent<RenderVis>();
            if (rendervis == null)
                continue;

            if (rendervis.IsVisible)
            {
                hasvis = true;
                break;
            }
        }
콘택트렌즈에 있는 Unit는 모든 렌더기를 옮겨다니며 렌더기가 OK인지 여부를 판단합니다. 렌더기가 이 Unit에 보일 때만 이 단원을 렌더링합니다.(렌더기의 가시성에 따라 각 렌더기를 별도로 렌더링하지 않는 것은 렌더링된 Unit가 부분적인 것이 아니라 전체적으로 렌더링되기 때문입니다. 전체적으로 렌더링된 것인지 아니면 렌더링되지 않은 것인지)
따라서 유닛이 언제 보이는지, 언제 보이지 않는지 어떻게 알 것인가가 문제다.다음 코드를 참조하십시오.
private bool mIsVisible = false;

    public bool IsVisible
    {
        get { return mIsVisible; }
    }

    void OnBecameVisible()
    {
        mIsVisible = true;
    }

    void OnBecameInvisible()
    {
        mIsVisible = false;
    }
이 스크립트는 각 렌더링 아래에서 중단됩니다.렌더기가 카메라에 표시되면 Unity 엔진에서 OnBecameVisible 함수를 호출합니다.렌더링 카메라가 표시되지 않으면 OnBecameInvisible 함수가 호출됩니다.
현재 Demo에서 투영 Camera가 CommandBuffer를 사용하면 Camera는 대상을 렌더링하지 않고 Main Camera만 모든 렌더링을 재현하기 때문에 Visible을 표시할 때 렌더링이 화면에 나타납니다.Vissible이 표시되지 않으면 이 렌더기는 화면에 표시되지 않습니다.
한 마디로 하면 프레임을 업데이트할 때 투영된 Camera를 표시할 수 있는 유닛을 먼저 선택한 다음 해당 객체가 Main Camera에 동시에 표시되는지 여부를 판단합니다.모두 만족하면 mCommandBuf.DrawRenderer(render、mReplaceMat);함수는 객체를 작성된 렌더기로 렌더링합니다.

3.5 프로젝터 Shader는 어떻게 설치됩니까?


투영 Shader는 실제로 그림자 수신 Shader로 구체적으로 다음과 같다.
ZWrite Off
            ColorMask RGB
            Blend DstColor Zero
            Offset -1, -1

            v2f vert(float4 vertex:POSITION)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.sproj = mul(unity_Projector, vertex);
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }

            float4 frag(v2f i):SV_TARGET
            {
                half4 shadowCol = tex2Dproj(_ShadowTex, UNITY_PROJ_COORD(i.sproj));
                half maskCol = tex2Dproj(_FalloffTex, UNITY_PROJ_COORD(i.sproj)).r;
                half a = shadowCol.r * maskCol;
                float c = 1.0 - _Intensity * a;

                UNITY_APPLY_FOG_COLOR(i.fogCoord, c, fixed4(1,1,1,1));

                return c;
            }
Vert에서 투영된 위치는 o.sproj=mull(unity Projector,vertex)로 계산됩니다.프레임에서 투영된 텍스쳐 좌표는 UNITY입니다.PROJ_COORD(i.sproj)를 기준으로 계산됩니다.그리고 최종 색깔을 섞는다.
다음과 같습니다.

마스크 이미지가 추가되어 섀시 쐐기의 페이드 및 페이드 처리가 보다 적합합니다.

4. 게임을 한다


다음과 같이 CommandBuffer를 사용하여 렌더링을 할지 여부를 동일한 디스플레이 각도로 전환합니다.같은 효과에서 CommandBuffer가 사용하는 Batch는 매우 우수하고 그에 상응하는 성능도 향상될 것이다.(위 이미지에는 CommandBuf가 없고 아래 이미지에는 CommandBuf가 사용됩니다.)

CommandBuf를 사용한 렌더링

2. 프로젝트 데모 주소


UWA 테크놀로지는 모바일/VR 등 다양한 게임 개발자를 대상으로 성능 분석과 최적화 솔루션 및 컨설팅 서비스를 제공하는 회사다.
이제 UWA GOT 로컬 도구는 15일 동안 무료로 사용할 수 있습니다!!
가능하다면, 반드시!
UWA 공식 사이트: https://jp.uwa4d.com
UWA GOT 온라인 보고서 데모: https://jp.uwa4d.com/u/got/demo.html
UWA 공식 블로그: https://blog.jp.uwa4d.com

좋은 웹페이지 즐겨찾기