eurigilberto / VolumetricCloudsUnityURP

This is a project I made for a technical art challenge. A volumetric cloud made in unity using a custom render feature in URP.
11 stars 1 forks source link

Clouds not rendering after importing the assets to a new project with URP already installed #2

Open strangeways-dev opened 1 year ago

strangeways-dev commented 1 year ago

This is the only way I could get this project in to Unity without being stuck in safe mode due to errors coming from the shader graph package but now the clouds aren't rendering. Any help would be appreciated, if I figure it out I will update this post and close the issue.

strangeways-dev commented 1 year ago

It seems we have to import the project settings too as they are essential to making the clouds render but after importing them the project keeps pushing errors every time the sceneview or editor is redrawn.

strangeways-dev commented 1 year ago

After a ton of research (and wasted time) I learned that you shouldn't use Blit() directly from a pass and you should never use CommandBuffer.Blit() (which would be cmd.Blit() in our case) with newer versions of URP, they are obsolete and will cause malfunctions. The only way we should use Blit() now is by using it from the Blitter class.

After updating the code to comply with the new methods all errors are gone but one warning remains, if I can solve the warning I think it should work.

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using static Unity.Burst.Intrinsics.X86.Avx;
using static UnityEngine.XR.XRDisplaySubsystem;

public class VolumetricRendering : ScriptableRendererFeature
{
    class CloudShadowRenderPass : ScriptableRenderPass
    {
        private Material cloudRenderer;
        private int materialPassIndex;
        private RTHandle cloudShadowTexture;
        private RenderTargetIdentifier cloudShadowRenderTarget = new RenderTargetIdentifier("_TempCloudShadowTexture");

        public CloudShadowRenderPass(Material cloudRenderer, int materialPassIndex) : base()
        {
            this.cloudRenderer = cloudRenderer;
            this.materialPassIndex = materialPassIndex;
            cloudShadowTexture = RTHandles.Alloc(cloudShadowRenderTarget, name: "_TempCloudShadowTexture");
        }

        // This method is called before executing the render pass.
        // It can be used to configure render targets and their clear state. Also to create temporary render target textures.
        // When empty this render pass will render to the active camera render target.
        // You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
        // The render pipeline will ensure target setup and clearing happens in a performant manner.
        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
        }

        // Here you can implement the rendering logic.
        // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
        // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
        // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get("RenderCloudShadow");

            RenderTextureDescriptor cameraTextureDesc = renderingData.cameraData.cameraTargetDescriptor;
            cameraTextureDesc.depthBufferBits = 0;

            cmd.GetTemporaryRT(Shader.PropertyToID(cloudShadowTexture.name), cameraTextureDesc, FilterMode.Bilinear);

            cmd.EnableShaderKeyword("_SHADOW_PASS");
            Blitter.BlitTexture(cmd, cloudShadowTexture, cloudShadowTexture, cloudRenderer, materialPassIndex);
            cmd.SetGlobalTexture("_ShadowPassTexture", cloudShadowTexture);

            //Execute and release commands
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        // Cleanup any allocated resources that were created during the execution of this render pass.
        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            //Release any temporary textures
            cmd.ReleaseTemporaryRT(Shader.PropertyToID(cloudShadowTexture.name));
        }
    }

    class CloudsPass : ScriptableRenderPass
    {
        private Material cloudRenderer;
        private Material denoiseMaterial;
        private Material combineTextureMaterial;
        private int materialPassIndex;
        private RenderTargetIdentifier _source;
        public RenderTargetIdentifier source
        {
            set
            {
                _source = value;
            }
        }
        public RTHandle cloudTexture;
        private RTHandle denoisedCloudTexture;
        private RTHandle sourceCopy;
        private RenderTargetIdentifier cloudTextureRenderTarget = new RenderTargetIdentifier("_TempVolumetricTexture");
        private RenderTargetIdentifier denoisedCloudTextureRenderTarget = new RenderTargetIdentifier("_TempDenoisedCloudTexture");
        private RenderTargetIdentifier sourceCopyRenderTarget = new RenderTargetIdentifier("_TempSourceCopy");

        public CloudsPass(Material cloudRenderer, int materialPassIndex, Material combineTextureMat, Material denoiseMaterial) : base()
        {
            this.materialPassIndex = materialPassIndex;
            this.cloudRenderer = cloudRenderer;
            this.denoiseMaterial = denoiseMaterial;
            this.combineTextureMaterial = combineTextureMat;
            cloudTexture = RTHandles.Alloc(cloudTextureRenderTarget, name: "_TempVolumetricTexture");
            denoisedCloudTexture = RTHandles.Alloc(denoisedCloudTextureRenderTarget, name: "_TempDenoisedCloudTexture");
            sourceCopy = RTHandles.Alloc(sourceCopyRenderTarget, name: "_TempSourceCopy");
        }

        // This method is called before executing the render pass.
        // It can be used to configure render targets and their clear state. Also to create temporary render target textures.
        // When empty this render pass will render to the active camera render target.
        // You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
        // The render pipeline will ensure target setup and clearing happens in a performant manner.
        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
        }

        // Here you can implement the rendering logic.
        // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
        // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
        // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
        //[System.Obsolete]
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get("RenderClouds");

            RenderTextureDescriptor cameraTextureDesc = renderingData.cameraData.cameraTargetDescriptor;
            cameraTextureDesc.depthBufferBits = 0;

            int width = Mathf.RoundToInt((float)cameraTextureDesc.width / 2f);
            int height = Mathf.RoundToInt((float)cameraTextureDesc.height / 2f);

            cmd.GetTemporaryRT(Shader.PropertyToID(cloudTexture.name), width, height, 0, FilterMode.Bilinear);
            cmd.GetTemporaryRT(Shader.PropertyToID(denoisedCloudTexture.name), width, height, 0, FilterMode.Bilinear);
            cmd.GetTemporaryRT(Shader.PropertyToID(sourceCopy.name), cameraTextureDesc, FilterMode.Bilinear);

            cmd.DisableShaderKeyword("_SHADOW_PASS");
            Blitter.BlitTexture(cmd, _source, cloudTexture, cloudRenderer, materialPassIndex);

            cmd.SetGlobalVector("_ImageSize", new Vector4(width, height, 0, 0));

            Blitter.BlitTexture(cmd, cloudTexture, denoisedCloudTexture, denoiseMaterial, materialPassIndex);
            cmd.SetGlobalTexture("_CloudTex", denoisedCloudTexture);

            Blitter.BlitTexture(cmd, _source, sourceCopy, combineTextureMaterial, materialPassIndex);
            //Blitter.BlitTexture(cmd, sourceCopy, _source); // (THIS MUGHT NOT BE NEEDED & HOPEFULLY NOT BECAUSE NEEDS MORE THAN 3 ARGUMENTS)

            //Execute and release commands
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        // Cleanup any allocated resources that were created during the execution of this render pass.
        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            //Release any temporary textures
            cmd.ReleaseTemporaryRT(Shader.PropertyToID(cloudTexture.name));
            cmd.ReleaseTemporaryRT(Shader.PropertyToID(denoisedCloudTexture.name));
            cmd.ReleaseTemporaryRT(Shader.PropertyToID(sourceCopy.name));
        }
    }

    [System.Serializable]
    public class Settings
    {
        public Material cloudRenderer;
        public Material denoiseMaterial;
        public Material combineTexture;
        public int materialPassIndex = -1;
    }

    [SerializeField]
    public Settings settings = new Settings();

    CloudShadowRenderPass cloudShadowRenderPass;
    CloudsPass cloudsPass;

    /// <inheritdoc/>
    public override void Create()
    {
        cloudShadowRenderPass = new CloudShadowRenderPass(settings.cloudRenderer, settings.materialPassIndex);
        cloudsPass = new CloudsPass(settings.cloudRenderer, settings.materialPassIndex, settings.combineTexture, settings.denoiseMaterial);

        // Configures where the render pass should be injected.
        cloudShadowRenderPass.renderPassEvent = RenderPassEvent.AfterRenderingPrePasses;
        cloudsPass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
    }

    public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
    {
        if (settings.cloudRenderer == null)
        {
            Debug.LogWarningFormat("Missing Blit Material. {0} blit pass will not execute. Check for missing reference in the assigned renderer.", GetType().Name);
            return;
        }

        cloudsPass.source = renderer.cameraColorTargetHandle;
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (settings.cloudRenderer == null)
        {
            Debug.LogWarningFormat("Missing Blit Material. {0} blit pass will not execute. Check for missing reference in the assigned renderer.", GetType().Name);
            return;
        }

        renderer.EnqueuePass(cloudShadowRenderPass);
        renderer.EnqueuePass(cloudsPass);
    }
}
strangeways-dev commented 1 year ago

I've narrowed it down and figure out that public void SetupRenderPasses is never being called. Supposedly you are suppose to use public override void SetupRenderPasses but when I try to do that there's a new problem.

A new error saying: 'VolumetricRendering.SetupRenderPasses(ScriptableRenderer, ref RenderingData)': no suitable method found to override

Not really sure how to solve this. Waiting for help from Unity forums, it seems like my only hope atm.

strangeways-dev commented 1 year ago

I have now got the SetupRenderPasses() to call by swapping 'ref for 'in' for the RenderingData parameter. So atleast now the methods is being called but the warning still prevails.

I will update the code above with the changes.

strangeways-dev commented 1 year ago

I got rid of the warning by using cloudsPass.source = renderer.cameraColorTargetHandle; in SetupRenderPasses()

There are no more errors or warnings but the clouds are still not rendering.

I have updated the code above with the changes. Not too sure where to go from here so I will just keep tinkering.