Setting textures in GLSL

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
xDan
Competition winner
Posts: 673
Joined: Thu Mar 30, 2006 1:23 pm
Location: UK
Contact:

Setting textures in GLSL

Post by xDan »

I'm just learning shaders.

I gather from other posts that to use a node's texture you just declare it

uniform sampler2D texture;

and then...

gl_FragColor = gl_Color * texture2D(texture, vec2(gl_TexCoord[0]));

and texture is somehow automatically set to the node's first texture? Is this correct?
(Can you point me to where in the irrlicht source this is done?)

But my main problem is that when I do that nodes without textures have a texture put on them.

So is there a way I can do something like...

gl_FragColor = gl_Color;
if (texture) // somehow checks if irrlicht has set a texture?
{
gl_FragColor *= texture2D(texture, vec2(gl_TexCoord[0]));
}

?
Kalda
Posts: 47
Joined: Wed Aug 23, 2006 1:38 pm
Location: Prostejov, Czech Republic
Contact:

Post by Kalda »

In irrlicht can material have 4 textures and sampler is number identifiing texture... First texture of material has number 0, last texture has 3. When you can set first texture, sampler must contains zero and when sampler is zero then the condition if(texture) is same as if(0) and this is the problem... For your code try set texture to second material layer using

Code: Select all

//in init
node->setMaterialTexture(1, texture);
//in shader constant callback
int texture = 1;
setPixelShaderConstant("texture",&texture,1);
xDan
Competition winner
Posts: 673
Joined: Thu Mar 30, 2006 1:23 pm
Location: UK
Contact:

Post by xDan »

Hi!

I realise if (texture) doesn't work ("boolean expression expected"), that was just a kind of example for what I need. Maybe "isset(texture)" would be more appropriate...

I know I could get around this by, say, using two different materials, one passing an IsTextured constant as true and the other as false. But this is kind of annoying, I wonder that it can't be simpler. Because the default Irrlicht material EMT_SOLID has the desired effect: if a texture is set, then it's textured, otherwise it just uses vertex colours or whatever. Logical! But maybe it's because shaders aren't used for that material. Maybe what I want is not possible?
xDan
Competition winner
Posts: 673
Joined: Thu Mar 30, 2006 1:23 pm
Location: UK
Contact:

Post by xDan »

I thought perhaps I can use an IMaterialRenderer for this. But no luck??? I'm not really sure how the shader fits into the material renderer. I tried this:

In the constructor, where "this" derives from IMaterialRenderer

Code: Select all

		SHADER_MATERIAL_STANDARD = driver->addMaterialRenderer(this, "SHADER_MATERIAL_STANDARD");
		
        video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
        
        SHADER_MATERIAL_STANDARD = gpu->addHighLevelShaderMaterialFromFiles(
			"media/shaders/hemi.vert", "main", video::EVST_VS_1_1,
			"media/shaders/hemi.frag", "main", video::EPST_PS_1_1,
			0, (video::E_MATERIAL_TYPE)SHADER_MATERIAL_STANDARD, 0);
Then I set all shader constants in OnSetMaterial. This works as good as it did before, however...

material.Textures[0] is always zero :/

For instance I have this OnSetMaterial:

Code: Select all

void Shader::OnSetMaterial(video::SMaterial &material, const video::SMaterial &lastMaterial,
    bool resetAllRenderstates, video::IMaterialRendererServices *services)
{
    if (material.MaterialType == SHADER_MATERIAL_STANDARD)
    {
        // Texture
        f32 useTexture0 = (int)material.Textures[0];
        
        if (material.Textures[0])
        {
            printf("USING TEXTURE\n");
        }
        
        services->setPixelShaderConstant("UseTexture0", (f32 *)(&useTexture0), 1);
        
        int tex0 = 0;
        services->setPixelShaderConstant("texture0", (f32 *)(&tex0), 1);
        
        // Hemisphere Lighting
        
        f32 sunPos[3] = {0.0,100.0,0.0};
        f32 skyCol[3] = {0xee/256.0, 0xee/256.0, 0x88/256.0};
        f32 groundCol[3] = {0x55/256.0, 0x55/256.0, 0x77/256.0};
        services->setVertexShaderConstant("SunPos", sunPos, 3);
        services->setVertexShaderConstant("SkyCol", skyCol, 3);
        services->setVertexShaderConstant("GroundCol", groundCol, 3);
    }
    
    services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
}
"Using Texture" is never printed :/ But I was under the impression that the passed SMaterial was from whatever node is currently rendering. And some of them definitely do have materials.


Bah. Well it's all really beyond me. Too confusing :(

It seems other people have had similar problems? For instance: http://irrlicht.sourceforge.net/phpBB2/ ... alrenderer
omaremad
Competition winner
Posts: 1027
Joined: Fri Jul 15, 2005 11:30 pm
Location: Cairo,Egypt

Post by omaremad »

You cant do this stuff in shaders. Anyway checking the exsistance of texture samplers in shaders is pointless since you know what textures you are feeding it ( you reallllly should because a shader will handles diffrent samplers in many ways other than simple uv texturing).
"Irrlicht is obese"

If you want modern rendering techniques learn how to make them or go to the engine next door =p
xDan
Competition winner
Posts: 673
Joined: Thu Mar 30, 2006 1:23 pm
Location: UK
Contact:

Post by xDan »

OK well this is what I've come up with in case anyone else encounters this problem. Based on what Luben said here.

Shader.h

Code: Select all

#ifndef __Shader_h
#define __Shader_h

#include <irrlicht.h>

using namespace irr;

// Declare new materials externally for use by scene nodes
extern s32 SHADER_MATERIAL_BASE;
extern s32 SHADER_MATERIAL_STANDARD;
extern s32 SHADER_MATERIAL_ANOTHER_EXAMPLE;

class Shader : public video::IMaterialRenderer, public video::IShaderConstantSetCallBack
{
public:
    Shader(IrrlichtDevice *);
    
    // if shaders were successfully created
    bool areAvailable();
    
    void OnSetConstants(video::IMaterialRendererServices *, s32);
    
    void OnSetMaterial(video::SMaterial &, const video::SMaterial &, bool, video::IMaterialRendererServices *);
    bool OnRender(video::IMaterialRendererServices *, video::E_VERTEX_TYPE);
    void OnUnsetMaterial();
    bool isTransparent();
    s32 getRenderCapability();
    
private:
    video::IVideoDriver *driver;
    video::IMaterialRenderer *baseMaterialRenderer;
    video::SMaterial *currentMaterial;
    bool shadersAvailable;
};

#endif
Shader.cpp

Code: Select all

#include "Shader.h"

// Shader material definitions

s32 SHADER_MATERIAL_BASE = video::EMT_SOLID;
s32 SHADER_MATERIAL_STANDARD = video::EMT_SOLID;
s32 SHADER_MATERIAL_ANOTHER_EXAMPLE = video::EMT_SOLID;

Shader::Shader(IrrlichtDevice *device)
{
    driver = device->getVideoDriver();
    currentMaterial = NULL;
    
    // Initialise shaders
    
	shadersAvailable =
        driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)
		&& driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1);
	
	if (shadersAvailable)
	{
        video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
        
        SHADER_MATERIAL_BASE = gpu->addHighLevelShaderMaterialFromFiles(
			"media/shaders/standard.vert", "main", video::EVST_VS_1_1,
			"media/shaders/standard.frag", "main", video::EPST_PS_1_1,
			this, video::EMT_SOLID, 0);
		
		SHADER_MATERIAL_STANDARD = driver->addMaterialRenderer(this, "SHADER_MATERIAL_STANDARD");
		SHADER_MATERIAL_ANOTHER_EXAMPLE = driver->addMaterialRenderer(this, "SHADER MATERIAL EXAMPLE");
	}
	else
	{
        printf("Warning: Shaders disabled because of missing driver/hardware support.\n");
    }
    
    baseMaterialRenderer = driver->getMaterialRenderer(SHADER_MATERIAL_BASE);
}

bool Shader::areAvailable()
{
    return shadersAvailable;
}

void Shader::OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
{
    if (!currentMaterial)
    {
        printf("Shader::OnSetConstants: Error: no currentMaterial.\n");
        return;
    }
    
    // Now send different parameters to shader based on state of currentMaterial.
    
    if (currentMaterial->MaterialType == SHADER_MATERIAL_STANDARD)
    {
        // Use Texture?
        f32 useTexture0 = currentMaterial->Textures[0] ? 1.0 : 0.0;
        services->setPixelShaderConstant("UseTexture0", &useTexture0, 1);
        
        // Set Texture 0
        int tex0 = 0;
        services->setPixelShaderConstant("texture0", (f32 *)(&tex0), 1);
    
        // Hemisphere Lighting
    
        f32 sunPos[3] = {0.0,100.0,0.0};
        f32 skyCol[3] = {0xee/256.0, 0xee/256.0, 0x88/256.0};
        f32 groundCol[3] = {0x55/256.0, 0x55/256.0, 0x77/256.0};
        services->setVertexShaderConstant("SunPos", sunPos, 3);
        services->setVertexShaderConstant("SkyCol", skyCol, 3);
        services->setVertexShaderConstant("GroundCol", groundCol, 3);
    }
    else if (currentMaterial->MaterialType == SHADER_MATERIAL_ANOTHER_EXAMPLE)
    {
        printf("Shader: Example material used!! :O\n");
    }
    else
    {
        printf("Shader::OnSetConstants: Error: Undefined material!\n");
    }
}

void Shader::OnSetMaterial(video::SMaterial &material, const video::SMaterial &lastMaterial,
    bool resetAllRenderstates, video::IMaterialRendererServices *services)
{
    // save current material for use by shader
    currentMaterial = &material;
    baseMaterialRenderer->OnSetMaterial(material, lastMaterial, resetAllRenderstates, services);
}

bool Shader::OnRender(video::IMaterialRendererServices *services, video::E_VERTEX_TYPE vtxtype)
{
    return baseMaterialRenderer->OnRender(services, vtxtype);
}

void Shader::OnUnsetMaterial()
{
    baseMaterialRenderer->OnUnsetMaterial();
}

bool Shader::isTransparent()
{
    return baseMaterialRenderer->isTransparent();
}

s32 Shader::getRenderCapability()
{
    return baseMaterialRenderer->getRenderCapability();
}
So all you do then is create an instance of that class and then you can use the materials defined in the header... and set the shader constants as usual in OnSetConstants but now you have a pointer to the current material.

But it would be much nicer if you could either set a shader in a material renderer and/or also have access to the current material from OnSetConstants...
(And no I've no idea how to make these modifications myself ;))
Post Reply