0
\$\begingroup\$

I am working on a procedural interior mapping shader in Unity's Shader Graph. Ideally, I'd like to feed it a set of cubemaps it can pick from semi-randomly.

However, it seems that by default the texture array asset one can provide to a graph does not support reading cubemaps.

Does anyone have a way to use this, or perhaps a clever custom node or alternate texture mapping, so that I can read a number of cubemaps into the shader semi-randomly? Preferably an arbitrary number, but even a fixed number would be somewhat of an improvement.

\$\endgroup\$
2
  • \$\begingroup\$ Have you tried using a custom shader code node to just write the code you want it to execute directly? \$\endgroup\$
    – DMGregory
    Mar 3, 2021 at 12:21
  • \$\begingroup\$ @DMGregory I don't believe custom input types are generally possible? And I am not a wiz at actually writing shader code (hence my use of SG at all, really). Perhaps I'm a little tunnel visioned because of it. \$\endgroup\$
    – Weckar E.
    Mar 3, 2021 at 14:50

1 Answer 1

1
\$\begingroup\$

We can use the Custom Function Node to work around limitations of the existing node types.

First, we'll make a new text file somewhere in our Assets folder. I called mine "CubeMapArray.cginc". Inside we can put just these nine lines of code:

TEXTURECUBE_ARRAY(_CubeArray);

void sample_cube_array_float(SamplerState ss, float3 coord3, float index, out float4 result) {
    result = SAMPLE_TEXTURECUBE_ARRAY(_CubeArray, ss, coord3, index);
}

void sample_cube_array_half(SamplerState ss, half3 coord3, half index, out half4 result) {
    result = SAMPLE_TEXTURECUBE_ARRAY(_CubeArray, ss, coord3, index);
}

You can see this defines a uniform variable _CubeArray that represents an array of cubemaps. Then it defines two versions of a function to sample that array - one at full float precision, and one at half precision. (I looked up the macros to use from this file)

Inside the shader graph editor, we'll create a new Custom Function Node and configure it like so:

Configuring Custom Function Node

Now unfortunately since we had to smuggle in our cubemap array texture via a uniform declared in code, we can't just assign the maps through the inspector. So we'll use a short C# script to copy our cubemaps into one texture array and assign that:

public class CubeMapArrayTest : MonoBehaviour
{
    public Cubemap[] maps;

    CubemapArray _array;
    Material _material;

    private void Start() {
        if (!SystemInfo.supportsCubemapArrayTextures) {
            Debug.LogError("System does not support cubemap array textures - this will not work!");
            return;
        }

        _array = new CubemapArray(maps[0].width, maps.Length, maps[0].format, true);

        for (int i = 0; i < maps.Length; i++)
            for (int j = 0; j < 6; j++)
                Graphics.CopyTexture(maps[i], j, _array, 6*i+j);

        _material = GetComponent<Renderer>().material;

        _material.SetTexture("_CubeArray", _array);
    }

    private void OnDestroy() {
        if (_material != null) {
            Destroy(_material);
            DestroyImmediate(_array);
        }
    }
}

On my machine this displays an error and warning in the console:

🛑 Shader error in 'hidden/preview/CustomFunction_DD7E970E': TextureCubeArray textures aren't supported on this target at Assets/ShaderEffects/CubeMapArray.cginc(1) (on d3d11)

⚠️ Shader warning in 'hidden/preview/CustomFunction_DD7E970E': 'TransformObjectToWorld': implicit truncation of vector type at line 85 (on d3d11)

But the shader still works correctly anyway. 😕 I'd love some insight into why that is and how to resolve or suppress those messages for peace of mind, but they don't seem to be an obstacle to solving the problem at least.


Update: Shader Graph version 10 accepts the above without complaint if it was imported from a Unity Package made in an earlier version (applying some invisible compatibility mode behind the scenes). But if you're creating this from scratch in a new shader graph in the latest version, you'll need to modify the code slightly:

TEXTURECUBE_ARRAY(_CubeArray);

void sample_cube_array_float(UnitySamplerState ss, float3 coord3, float index, out float4 result) {
    result = SAMPLE_TEXTURECUBE_ARRAY(_CubeArray, ss.samplerstate, coord3, index);
}

void sample_cube_array_half(UnitySamplerState ss, half3 coord3, half index, out half4 result) {
    result = SAMPLE_TEXTURECUBE_ARRAY(_CubeArray, ss.samplerstate, coord3, index);
}

You'll still get console errors about cube map arrays being unsupported when opening the shader graph window, but it works in play mode, even in URP (tested in 2020.3.31f).

\$\endgroup\$
2
  • \$\begingroup\$ Works like a charm. If I figure out why those errors happen I'll let you know... \$\endgroup\$
    – Weckar E.
    Mar 4, 2021 at 4:50
  • \$\begingroup\$ @DMGregory Hi, this solution don't seem to work in sg version 10. Here is a screenshot of the result, and this is what the error says: Shader error in 'Shader Graphs/array tst': 'sample_cube_array_float': cannot convert from 'const struct UnitySamplerState' to 'SamplerState' at line 2086 (on d3d11) I'm noob so any help is appreciated :) \$\endgroup\$
    – MamduCoder
    Apr 8, 2022 at 5:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .