Search Results for

    Show / Hide Table of Contents

    HOWTO: Generate a procedural texture

    Texture

    In this tutorial, you will learn how to create a simple texture from C# script code.

    This sample uses the C# API method Content.CreateVirtualAsset to generate a procedural texture resource which can be modified at runtime from code.

    Tutorial

    1. Create a new C# script named TextureFromCode

    2. Write the texture data generating code

    • C#
    • C++
    public class TextureFromCode : Script
    {
        private Texture _tempTexture;
        private MaterialInstance _tempMaterialInstance;
    
        public Material Material;
        public Model Model;
    
        public override unsafe void OnStart()
        {
            // Ensure that model asset is loaded
            if (Model.WaitForLoaded())
                return;
    
            // Create new texture asset
            var texture = Content.CreateVirtualAsset<Texture>();
            _tempTexture = texture;
            var initData = new TextureBase.InitData();
            initData.Width = 64;
            initData.Height = 64;
            initData.ArraySize = 1;
            initData.Format = PixelFormat.R8G8B8A8_UNorm;
            var data = new byte[initData.Width * initData.Height * PixelFormatExtensions.SizeInBytes(initData.Format)];
            fixed (byte* dataPtr = data)
            {
                // Generate pixels data (linear gradient)
                var colorsPtr = (Color32*)dataPtr;
                for (int y = 0; y < initData.Height; y++)
                {
                    float t1 = (float)y / initData.Height;
                    var c1 = Color32.Lerp(Color.Red, Color.Blue, t1);
                    var c2 = Color32.Lerp(Color.Yellow, Color.Green, t1);
                    for (int x = 0; x < initData.Width; x++)
                    {
                        float t2 = (float)x / initData.Width;
                        colorsPtr[y * initData.Width + x] = Color32.Lerp(c1, c2, t2);
                    }
                }
            }
            initData.Mips = new[]
            {
                // Initialize mip maps data container description
                new TextureBase.InitData.MipData
                {
                    Data = data,
                    RowPitch = data.Length / initData.Height,
                    SlicePitch = data.Length
                },
            };
            if (texture.Init(ref initData))
                return;
    
            // Use a dynamic material instance with a texture to sample
            var material = Material.CreateVirtualInstance();
            _tempMaterialInstance = material;
            material.SetParameterValue("tex", texture);
    
            // Add a model actor and use the dynamic material for rendering
            var staticModel = Actor.GetOrAddChild<StaticModel>();
            staticModel.Model = Model;
            staticModel.SetMaterial(0, material);
        }
    
        public override void OnDestroy()
        {
            // Ensure to cleanup resources
            FlaxEngine.Object.Destroy(ref _tempTexture);
            FlaxEngine.Object.Destroy(ref _tempMaterialInstance);
        }
    }
    
    // .h
    #pragma once
    
    #include "Engine/Scripting/Script.h"
    #include "Engine/Content/AssetReference.h"
    #include "Engine/Content/Assets/Model.h"
    #include "Engine/Content/Assets/Material.h"
    
    API_CLASS() class GAME_API TextureFromCodeCpp : public Script
    {
        API_AUTO_SERIALIZATION();
        DECLARE_SCRIPTING_TYPE(TextureFromCodeCpp);
    private:
        Texture* _tempTexture = nullptr;
        MaterialInstance* _tempMaterialInstance = nullptr;
    
    public:
        // The custom material to set its texture.
        API_FIELD() AssetReference<Material> Material;
        // The custom model to set its material.
        API_FIELD() AssetReference<Model> Model;
    
        // [Script]
        void OnStart() override;
        void OnDestroy() override;
    };
    
    // .cpp
    #include "TextureFromCodeCpp.h"
    #include "Engine/Core/Types/Variant.h"
    #include "Engine/Level/Actor.h"
    #include "Engine/Content/Content.h"
    #include "Engine/Content/Assets/MaterialInstance.h"
    #include "Engine/Graphics/PixelFormatExtensions.h"
    #include "Engine/Level/Actors/StaticModel.h"
    
    TextureFromCodeCpp::TextureFromCodeCpp(const SpawnParams& params)
        : Script(params)
    {
    }
    
    void TextureFromCodeCpp::OnStart()
    {
        CHECK(Material);
        CHECK(Model);
    
        // Ensure that model asset is loaded
        if (Model->WaitForLoaded())
            return;
    
        // Create new texture asset
        auto texture = Content::CreateVirtualAsset<Texture>();
        _tempTexture = texture;
        TextureBase::InitData initData;
        initData.Width = 64;
        initData.Height = 64;
        initData.ArraySize = 1;
        initData.Format = PixelFormat::R8G8B8A8_UNorm;
        auto& mipData = initData.Mips.AddOne();
        {
            // Initialize mip maps data container description
            mipData.Data.Allocate(initData.Width * initData.Height * PixelFormatExtensions::SizeInBytes(initData.Format));
            mipData.RowPitch = mipData.Data.Length() / initData.Height;
            mipData.SlicePitch = mipData.Data.Length();
        }
        byte* data = mipData.Data.Get();
        {
            // Generate pixels data (linear gradient)
            auto colorsPtr = (Color32*)data;
            for (int y = 0; y < initData.Height; y++)
            {
                float t1 = (float)y / initData.Height;
                Color c1 = Color::Lerp(Color::Red, Color::Blue, t1);
                Color c2 = Color::Lerp(Color::Yellow, Color::Green, t1);
                for (int x = 0; x < initData.Width; x++)
                {
                    float t2 = (float)x / initData.Width;
                    colorsPtr[y * initData.Width + x] = Color32(Color::Lerp(c1, c2, t2));
                }
            }
        }
        if (texture->Init(MoveTemp(initData)))
            return;
    
        // Use a dynamic material instance with a texture to sample
        auto material = Material->CreateVirtualInstance();
        _tempMaterialInstance = material;
        material->SetParameterValue(TEXT("tex"), Variant(texture));
    
        // Add a model actor and use the dynamic material for rendering
        auto staticModel = GetActor()->GetOrAddChild<StaticModel>();
        staticModel->Model = Model;
        staticModel->SetMaterial(0, material);
    }
    
    void TextureFromCodeCpp::OnDestroy()
    {
        // Ensure to cleanup resources
        SAFE_DELETE(_tempMaterialInstance);
        SAFE_DELETE(_tempTexture);
    }
    

    3. Create the material

    Create a simple material that contains a public texture parameter named tex. It's used by the script to assign a texture to draw.

    Material

    4. Link the material and model

    Add created script TextureFromCode to an actor in your scene (or create a new one for it). Then select the actor and assign the model and created material (as shown in a picture below).

    Link Material and Model

    5. Test it out!

    Press Play (or F5) and see the results!

    Results

    • Improve this Doc
    In This Article
    Back to top Copyright © 2012-2024 Wojciech Figat