IrrlichtLime: How to set textures in a shader for postproces

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
shadoh
Posts: 3
Joined: Thu Mar 29, 2018 3:06 am

IrrlichtLime: How to set textures in a shader for postproces

Post by shadoh »

I'm trying to do some post processing in my game, but I can't seem to get it going.

Problem A:
No UV information appears to be sent, or maybe I'm accessing it wrong, I've temporarily got the uv mapping working based on vertex position in the vertex shader. But it's quite hackish.

Problem B:
Parsing textures to the uniforms in the fragment shader. I just can't figure it out. Below I've included my post processing class, and the shader, and anything else you possibly need to give me advice. It looks so simple in the base C++ version, outside of the wrapper, but there appears to be no entry point in IrrlichtLime.

PostProcess class (Puts all the post processing mundane stuff in one place). (Note the SetTexture function, probably wrong)

Code: Select all

/// <summary>
    /// PostProcess class is based on QuadRender class, I lost reference to
    /// </summary>
    class PostProcess
    {
        IrrlichtDevice dev;
        VideoDriver vid;
 
        Material material;
        Vertex3D[] vert;
        ushort[] index;
 
        ShaderCallBack shader;
 
        public PostProcess(IrrlichtDevice device, string vertShader, string fragShader)
        {
            dev = device;
            vid = device.VideoDriver;
 
            shader = vid.GPUProgrammingServices.AddHighLevelShaderMaterialFromFiles(
                vertShader, "main", VertexShaderType.VS_4_0,
                fragShader, "main", PixelShaderType.PS_4_0,
                MaterialType.DetailMap, 0, GPUShadingLanguage.Default);
 
            if (shader == null)    //if we got a shader callback (shader was created successfully)
            {
                Program.debug.LogText("Failed to load shader: " + vertShader + "/" + fragShader);
                throw new Exception("Failed to load shader, check log for details");
            } else
            {
                // material for screen quad
                material = new Material();
                material.Wireframe = false;
                material.Lighting = false;
                material.SetFlag(MaterialFlag.ZWrite, false);
                material.Type = shader;
            }
 
            // screen quad
            vert = new Vertex3D[4] {
                new Vertex3D(-1, -1, 0, 0, 0, 1f, Color.OpaqueRed , 0, 1),
                new Vertex3D(-1, 1, 0, 0, 0, 1, Color.OpaqueGreen, 0, 0),
                new Vertex3D(1, 1, 0, 0, 0, 1, Color.OpaqueBlue, 1, 0),
                new Vertex3D(1, -1, 0, 0, 0, 1, Color.OpaqueWhite, 1, 1),
            };
            index = new ushort[6]
            {
                0,1,2,
                0,2,3
            };
        }
 
        /// <summary>
        /// Sets texture in shader
        /// </summary>
        /// <param name="id">Uniform ID location (I don't see any access to retrieving via name)</param>
        /// <param name="tex">Texture to set in shader</param>
        public void SetTexture(int id, Texture tex)
        {
            material.SetTexture(id, tex);
        }
 
        /// <summary>
        /// Render, called during draw routine
        /// </summary>
        public void Render()
        {
            vid.SetMaterial(material);
            vid.SetTransform(TransformationState.World, new Matrix());
            vid.DrawVertexPrimitiveList(vert, index);
        }
 
 
        
    }
Render loop (pp is the PostProcess object)

Code: Select all

while (device.Run())
            {
                Input.FrameUpdate();
                if (device.WindowActive)
                    mainWorld.Update();
 
                device.SceneManager.VideoDriver.BeginScene(ClearBufferFlag.All, new Color(100, 101, 140));
 
                mainWorld.PreRender();
 
                //Draw world to render target
                driver.SetRenderTargetEx(worldRT, ClearBufferFlag.All, Color.OpaqueBlack);
                mainWorld.physicsWorld.DebugDrawWorld();
                device.SceneManager.DrawAll();
 
                
 
                //Draw render targets plus post process to screen
                driver.SetRenderTarget(null);
 
                pp.SetTexture(0, mainWorld.GetWaterTexture());
                pp.SetTexture(1, worldTex);
                pp.SetTexture(2, mainWorld.GetWaterDepthTexture());
                pp.SetTexture(3, worldDepth);
                pp.Render();
                
 
                device.GUIEnvironment.DrawAll();
 
                driver.Draw2DImage(worldTex, 
                    new Recti(0,0,128,128),new Recti(0,0,driver.ScreenSize.Width,driver.ScreenSize.Height));
                driver.Draw2DImage(mainWorld.GetWaterTexture(), 
                    new Recti(128, 0, 256, 128), new Recti(0, 0, driver.ScreenSize.Width, driver.ScreenSize.Height));
                driver.Draw2DImage(worldDepth, 
                    new Recti(256, 0, 384, 128), new Recti(0, 0, driver.ScreenSize.Width, driver.ScreenSize.Height));
                driver.Draw2DImage(mainWorld.GetWaterDepthTexture(), 
                    new Recti(384, 0, 512, 128), new Recti(0, 0, driver.ScreenSize.Width, driver.ScreenSize.Height));
 
                device.SceneManager.VideoDriver.EndScene();
            }
Vertex shader

Code: Select all

 
#version 400
//Note these locations were worked out by trial & error
in layout(location=0) vec3 vp;
in layout(location=3) vec4 col;
in layout(location=2) vec3 nm;
in layout(location=1) vec2 uv;
 
out vec2 tex_coord;
void main() {
  gl_Position = vec4(vp, 1.0);
  //tex_coord = vec2((vp.x + 1.0) * 0.5,(vp.y + 1.0) * 0.5);//Works, but should be using line below
  tex_coord = uv;//filled with 0,0
}
Fragment shader

Code: Select all

 
#version 400
 
//Here's what I need to set, how do I go about it?
uniform sampler2D waterRT;
uniform sampler2D worldRT;
uniform sampler2D waterDepth;
uniform sampler2D worldDepth;
 
in vec2 tex_coord;
out vec4 frag_colour;
void main() {
    vec4 water = texture(worldRT, tex_coord).rgba;
    vec4 world = texture(waterRT, tex_coord).rgba;
    float worD = texture(worldDepth, tex_coord).r;
    float watD = texture(waterDepth, tex_coord).r;
    frag_colour = world;
  
}
What the output looks like:
Image
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: IrrlichtLime: How to set textures in a shader for postpr

Post by CuteAlien »

In opengl shaders (at least in older ones, it's different in GL ES) you should get uv's from gl_MultiTexCoord0 variable. Take a look at the code from the Irrlicht shader example (opengl shaders for that are media/opengl.vert and media/opengl.frag).
For passing texture you need to send it inside a class derived from IShaderConstantSetCallBack. Again take a look at the Irrlicht shader example (10. Shaders) and search for "TextureID" - that's the parts involved in sending the texture.
I have no experience with c#, so don't know how to derive classes there and similar stuff, so I can only point you to the c++ examples.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
shadoh
Posts: 3
Joined: Thu Mar 29, 2018 3:06 am

Re: IrrlichtLime: How to set textures in a shader for postpr

Post by shadoh »

Yeah, all that stuff is there in the c++ version, I guess they didn't add it for IrrlichtLime. I'll try creating a QuadBuffer SceneNode, and sending it that way.

I even used Reflection to get the private variable in the Texture class so I can send the pointer straight in to MaterialRendererServices, but unfortunately it only accepts 32bit values, and I'm running 64bit.

Like this:

Code: Select all

private unsafe void SetTexture(MaterialRendererServices services,int id, Texture tex)
        {
            Type t = typeof(Texture);
            FieldInfo f = t.GetField("m_Texture",
                BindingFlags.NonPublic |
                BindingFlags.Instance);
            Pointer obj = (Pointer)f.GetValue(tex);
 
            var ptr = (IntPtr)Pointer.Unbox(obj);
 
            //services.SetPixelShaderConstant(id, @ptr); No way to set this :(
        }
Seems I can only set floats or ints :(

I'll try using a MeshBuffer instead, I think that might actually look at the materials I set.

EDIT: Ok, this is why programming late at night is a bad idea. I finally figured it out lol

In my defense I do believe this line in the example needs some kind of commenting:

Code: Select all

services.SetPixelShaderConstant(shaderTextureId, new int[] { 0 }, true);
Ok, so the number sent in the array, is the reference to the number in the material.
shaderTextureId is the uniform number which can be retrieved from services.GetVertexShaderConstantID("myTexture");

all in the OnSetConstants part of it. I was only either setting the material, or setting the MaterialRenderServices, I was not doing both :( Admin can move this to beginner section, cause I'm dumb and thought this just wasn't implemented lol

Thank you heaps for your reply, and telling me to go back to the books lol
shadoh
Posts: 3
Joined: Thu Mar 29, 2018 3:06 am

Re: IrrlichtLime: How to set textures in a shader for postpr

Post by shadoh »

For anybody else who is struggling with PostProcessing in IrrlichtLime. Here's my working PostProcess class, cheers :)

Code: Select all

 
    /// <summary>
    /// PostProcess class is based on QuadRender class, I lost reference to
    /// </summary>
    class PostProcess
    {
        IrrlichtDevice dev;
        VideoDriver vid;
 
        Material material;
        Vertex3D[] vert;
        ushort[] index;
 
        ShaderCallBack shader;
 
        struct STexture
        {
            public Texture tex;
            public int id;
        }
 
        Dictionary<string, STexture> textures = new Dictionary<string, STexture>();
 
        public PostProcess(IrrlichtDevice device, string vertShader, string fragShader)
        {
            dev = device;
            vid = device.VideoDriver;
 
            shader = vid.GPUProgrammingServices.AddHighLevelShaderMaterialFromFiles(
                vertShader, "main", VertexShaderType.VS_4_0,
                fragShader, "main", PixelShaderType.PS_4_0,
                MaterialType.DetailMap, 0, GPUShadingLanguage.Default);
 
            if (shader == null)    //if we got a shader callback (shader was created successfully)
            {
                //Program.debug.LogText("Failed to load shader: " + vertShader + "/" + fragShader);
                throw new Exception("Failed to load shader, check log for details");
            } else
            {
                // material for screen quad
                material = new Material();
                material.Wireframe = false;
                material.Lighting = false;
                material.SetFlag(MaterialFlag.ZWrite, false);
                material.Type = shader;
                shader.OnSetConstants += Shader_OnSetConstants;
            }
 
            // screen quad
            vert = new Vertex3D[4] {
                new Vertex3D(-1, -1, 0, 0, 0, 1f, Color.OpaqueRed , 0, 1),
                new Vertex3D(-1, 1, 0, 0, 0, 1, Color.OpaqueGreen, 0, 0),
                new Vertex3D(1, 1, 0, 0, 0, 1, Color.OpaqueBlue, 1, 0),
                new Vertex3D(1, -1, 0, 0, 0, 1, Color.OpaqueWhite, 1, 1),
            };
            index = new ushort[6]
            {
                0,1,2,
                0,2,3
            };
        }
        
        /// <summary>
        /// Sets the shader references to the materials
        /// </summary>
        private void Shader_OnSetConstants(MaterialRendererServices services, int userData)
        {
            foreach (string key in textures.Keys)
            {
                int id = services.GetPixelShaderConstantID(key);
                if (id != -1)
                {
                    STexture tex = textures[key];
 
                    services.SetPixelShaderConstant(id, new int[] { tex.id }, true);
                }
            }
        }
 
        /// <summary>
        /// Sets texture for use in post processing
        /// </summary>
        /// <param name="name">Name of the texture in the shader</param>
        /// <param name="number">Number of texture to use for the material</param>
        /// <param name="tex">The texture that will be set in the shader on render</param>
        public void SetTexture(string name, int number, Texture tex)
        {
            if (!textures.ContainsKey(name))
            {
                textures.Add(name, new STexture()
                {
                    id = number,
                    tex = tex
                });
            }
            else
            {
                textures[name] = new STexture()
                {
                    id = number,
                    tex = tex
                };
            }
        }
 
        /// <summary>
        /// Render, called during draw routine
        /// </summary>
        public void Render()
        {
            //Set material textures
            foreach (string key in textures.Keys)
            {
                var t = textures[key];
                material.SetTexture(t.id,t.tex);
            }
            vid.SetMaterial(material);
            vid.SetTransform(TransformationState.World, new Matrix());
            vid.DrawVertexPrimitiveList(vert, index);
        }
 
 
 
    }
}
 
        /// <param name="name">Name of the texture in the shader</param>
        /// <param name="number">Number of texture to use for the material</param>
        /// <param name="tex">The texture that will be set in the shader on render</param>
        public void SetTexture(string name, int number, Texture tex)
        {
            if (!textures.ContainsKey(name))
            {
                textures.Add(name, new STexture()
                {
                    id = number,
                    tex = tex
                });
            }
            else
            {
                textures[name] = new STexture()
                {
                    id = number,
                    tex = tex
                };
            }
        }
 
        /// <summary>
        /// Render, called during draw routine
        /// </summary>
        public void Render()
        {
            //Set material textures
            foreach (string key in textures.Keys)
            {
                var t = textures[key];
                material.SetTexture(t.id,t.tex);
            }
            vid.SetMaterial(material);
            vid.SetTransform(TransformationState.World, new Matrix());
            vid.DrawVertexPrimitiveList(vert, index);
        }
}
 
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: IrrlichtLime: How to set textures in a shader for postpr

Post by CuteAlien »

No worries, advanced section is fine for that kind of stuff :-) (those should maybe be merged anyway as no one is ever sure where to post...) Glad it worked out.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Post Reply