Use geometry data caster to change materials on a remeshed asset

Disclaimer: The code in this post is written using version 9.1.36100 of Simplygon. If you encounter this post at a later stage, some of the API calls might have changed. However, the core concepts should still remain valid.


Using the remeshing processor is a great way of optimizing a complex asset into something that can be used on a mobile platform or on the web. The remeshing processor will combine all input meshes into a new mesh and you can transfer all input materials into a combined material using material casters.

But what if you instead want to apply your original materials to the remeshed asset and even be able to change the materials in runtime?

In this post we’ll demonstrate exactly that where we apply original materials to the remeshed asset in runtime using geometry data casters.


This example will use the Simplygon integration in Unity, but the same concepts can be applied to all game engines using the Simplygon API.

Problem to solve

We’ll use the following chair as input asset, and we want to be able to change seat and leg materials in runtime.
To cast every material combination to new textures is not an option due to memory constraints so we want to use the original materials instead and change the materials using a custom shader in runtime.
Input asset


Let us start optimizing the input asset using the remeshing processor. This will reduce the number of triangles and create a single mesh we can render with just one draw call. Wireframe
Here you can see the difference in complexity between the original and remeshed object.

But if we try to apply the original materials to the remeshed object it will look like garbage. This is because the remeshed object has a completely new set of texcoords and all material ids are lost.

Here you can see the difference in texcoords between the original and the remeshed object.

Geometry data casting

This is where the geometry data caster comes in. Instead of casting regular colors or normals it casts other types of geometry data like texcoords and material ids.
A complete list of supported data types can be found in the geometry data caster documentation.


Texture compression like DXT will create artifacts when you sample these textures in a shader so please use an uncompressed texture format.
And to preserve as much detail as possible of the original texcoords we’ll cast the texcoords to a 16bit per channel texture format.

Let us create two geometry data casters, one for texcoords and one for material ids with the following settings.

We can now use these textures in a custom shader to map geometry data from the remeshed asset back to the input asset.

Here you can see a comparison between the original texcoords and the remeshed texcoords using the texcoords texture created by the geometry data caster to map the texcoords back to the original.

Here you can see the original material ids applied to the remeshed object using the material id texture created by the geometry data caster and represented by color values.


With the newly created textures by the geometry data caster, we can combine everything into a custom shader that maps both texcoords and material ids back to the input asset and applies original materials in runtime.

Complete Unity shader

Shader "GeometryDataCaster/RunTimeShader"
        //Settings to change material variants
        _SeatVariant("Seat variant", Int) = 0
        _LegsVariant("Legs variant", Int) = 0

        //Textures created by the geometry data caster
        _GeometryDataTexCoordTex("TexCoord", 2D) = "white" {}
        _GeometryDataMaterialIdTex("MaterialId", 2D) = "white" {}

        //Original seat textures
        _SeatTex1 ("Seat variant 1", 2D) = "white" {}
        _SeatTex2 ("Seat variant 2", 2D) = "white" {}

        //Original legs textures
        _LegsTex1("Legs variant 1", 2D) = "white" {}
        _LegsTex2("Legs variant 2", 2D) = "white" {}
        Tags { "RenderType"="Opaque" }
        LOD 200

        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        half _SeatVariant;
        half _LegsVariant;

        sampler2D _GeometryDataTexCoordTex;
        sampler2D _GeometryDataMaterialIdTex;

        sampler2D _SeatTex1;
        sampler2D _SeatTex2;
        sampler2D _LegsTex1;
        sampler2D _LegsTex2;

        struct Input
            float2 uv_GeometryDataTexCoordTex;
            float2 uv_GeometryDataMaterialIdTex;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
            // put more per-instance properties here

        void surf (Input IN, inout SurfaceOutputStandard o)
            //Map texcoords back to original texcoords using geometry data caster texture
            float4 texCoord = tex2D(_GeometryDataTexCoordTex, IN.uv_GeometryDataTexCoordTex);
            //Map material ids back to original material ids using geometry data caster texture
            float materialId = tex2D(_GeometryDataMaterialIdTex, IN.uv_GeometryDataMaterialIdTex).r * 255.0f;

            if (materialId == 0) // Material 0 is seat
                if (_SeatVariant == 0)
                    o.Albedo = tex2D(_SeatTex1, texCoord.xy);
                    o.Albedo = tex2D(_SeatTex2, texCoord.xy);
            else if (materialId > 0) // Material 1 is legs
                if (_LegsVariant == 0)
                    o.Albedo = tex2D(_LegsTex1, texCoord.xy);
                    o.Albedo = tex2D(_LegsTex2, texCoord.xy);
            o.Alpha = 1.0f;
    FallBack "Diffuse"
⇐ Back to all posts

Request 30-days free evaluation license


Request 30-days free evaluation license