0
\$\begingroup\$

I want to implement a heightmap-based water simulation algorithm in Unity. I know exactly how I would approach this in pure low-level C++ and OpenGL (and also Shadertoy), and most of it is straightforward to port to Unity.

The algorithm needs terrain height information with a heightmap. I'd like it to be flexible, so I don't want to force the user to use a terrain heightmap that has to exactly fit the area where the water is supposed to go. Instead, I'd like the user to model terrain and other static geometry in any way they want and then be able to hit a button to directly bake that geometry to a heightmap by rendering the current editor scene to a texture that is then saved as a game asset. The ortographic volume for this would be specified on the prefab that I'd create for the water simulation.

So, in short, my question is: How can I trigger a render to texture of the current scene at editor time (and save this as an asset)? And, if possible, can I specify to render only objects with a given Tag (to avoid dynamic objects in the bake)?

\$\endgroup\$
1
\$\begingroup\$

This was actually more straightforward than I thought. Everything that is supposed to work at runtime seems to work fine from the Editor, too. I created a script on the camera called HeightmapCamera (that does not do anything) and then added this Editor-Script:

using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;

[CustomEditor(typeof(HeightmapCamera))]
public class EditorHeightmapCamera : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        if (GUILayout.Button("Build Object"))
        {
            var _cam = (HeightmapCamera)target;
            var rt = new RenderTexture(256, 256, 16, RenderTextureFormat.ARGB32);
            rt.Create();
            Camera cam = _cam.GetComponent<Camera>();
            cam.targetTexture = rt;
            cam.Render();
            cam.targetTexture = null;


            RenderTexture.active = rt;
            Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
            tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
            RenderTexture.active = null;

            byte[] bytes;
            bytes = tex.EncodeToPNG();

            string path = SceneManager.GetActiveScene().path.Split('.')[0] + _cam.GetHashCode() + ".png";
            System.IO.File.WriteAllBytes(path, bytes);
            AssetDatabase.ImportAsset(path);


            Debug.Log("Saved to " + path);
        }
    }
}

This example is just a prototype, but it succesfully creates a PNG in the folder where the scene is located. From here, it's just writing a shader that renders linear depth (depth buffer is already linear for orthographic cameras), as well as probably a 16-bit texture at least and improvements to the Editor UI that let the user choose the resolution and so on, but I'll consider my question answered with this.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.