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]));
}
?
Setting textures in GLSL
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);
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?
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?
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
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:
"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
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);
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);
}
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
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
If you want modern rendering techniques learn how to make them or go to the engine next door =p
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
Shader.cpp
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 )
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
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();
}
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 )