basic shader constant passing

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
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

basic shader constant passing

Post by Seven »

I am trying to learn shaders and have created a few for doing some nice visual effects on planes.
As part of y learning, i decided to add my own lighting (a single light) to see how uniforms and varying variables are passed around.
I then modified irrlicht to print out the uniforms available for the getVertexShaderConstantID calls and I cant figure out why some of the uniforms / varying are working fine but others are not.

i will post the code here and hope that someone with more knowledge can point me in the right direction.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

the shader callback
declare ID variables and in firstUpdate try to assign them to the GL indexes ( this is what seems to fail)

Code: Select all

	class IGE_ShaderCallback : public IShaderConstantSetCallBack
	{
	private:
		IGE_VARIABLE_SETGET(bool, FirstUpdate, true);
		IGE_VARIABLE_GET(IGE_Object*, Object, nullptr);

		// vertex constants
		IGE_VARIABLE_SETGET(s32, WorldViewProjID, -1);
		IGE_VARIABLE_SETGET(s32, TransWorldID, -1);
		IGE_VARIABLE_SETGET(s32, InvWorldID, -1);
		IGE_VARIABLE_SETGET(s32, AmbientLightID, -1);
		IGE_VARIABLE_SETGET(s32, CameraPositionID, -1);
		IGE_VARIABLE_SETGET(s32, MousePositionID, -1);
		IGE_VARIABLE_SETGET(s32, TimeID, -1);

		IGE_VARIABLE_SETGET(s32, LightPositionID, -1);
		IGE_VARIABLE_SETGET(s32, LightDiffuseID, -1);
		IGE_VARIABLE_SETGET(s32, LightSpecularID, -1);
		IGE_VARIABLE_SETGET(s32, LightAttenuationID, -1);

		// fragment constants
		IGE_VARIABLE_SETGET(s32, Texture0ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture1ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture2ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture3ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture4ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture5ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture6ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture7ID, -1);

	public:
		IGE_ShaderCallback(IGE_Object* obj) : IShaderConstantSetCallBack(), m_Object(obj) { }

		virtual stringc getDirectory(stringc dirname) { return m_Object == nullptr ? IGE_DEFAULT_STRING : m_Object->getDirectory(dirname); }

		virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
		{
			// get shader constants id.
			if (m_FirstUpdate)
			{
				m_WorldViewProjID = services->getVertexShaderConstantID("mWorldViewProj");
				m_TransWorldID = services->getVertexShaderConstantID("mTransWorld");
				m_InvWorldID = services->getVertexShaderConstantID("mInvWorld");

				m_AmbientLightID = services->getVertexShaderConstantID("mAmbientLight");
				m_CameraPositionID = services->getVertexShaderConstantID("mCameraPosition");
				m_TimeID = services->getVertexShaderConstantID("mTime");

				m_LightPositionID = services->getVertexShaderConstantID("mLightPosition");
				m_LightDiffuseID = services->getVertexShaderConstantID("mLightDiffuse");
				m_LightSpecularID = services->getVertexShaderConstantID("mLightSpecular");
				m_LightAttenuationID = services->getVertexShaderConstantID("mLightAttenuation");

				m_Texture0ID = services->getPixelShaderConstantID("Texture0");
				m_Texture1ID = services->getPixelShaderConstantID("Texture1");
				m_Texture2ID = services->getPixelShaderConstantID("Texture2");
				m_Texture3ID = services->getPixelShaderConstantID("Texture3");
				m_Texture4ID = services->getPixelShaderConstantID("Texture4");
				m_Texture5ID = services->getPixelShaderConstantID("Texture5");
				m_Texture6ID = services->getPixelShaderConstantID("Texture6");
				m_Texture7ID = services->getPixelShaderConstantID("Texture7");
			}

			// vertex shader constants
			// set inverted world matrix
			video::IVideoDriver* driver = services->getVideoDriver();

			// set clip matrix
			core::matrix4 worldViewProj;
			worldViewProj = driver->getTransform(video::ETS_PROJECTION);
			worldViewProj *= driver->getTransform(video::ETS_VIEW);
			worldViewProj *= driver->getTransform(video::ETS_WORLD);

			services->setVertexShaderConstant(m_WorldViewProjID, worldViewProj.pointer(), 16);

			// set world matrixes
			core::matrix4 world = driver->getTransform(video::ETS_WORLD);
			world = world.getTransposed();
			services->setVertexShaderConstant(m_TransWorldID, world.pointer(), 16);

			core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
			invWorld.makeInverse();
			services->setVertexShaderConstant(m_InvWorldID, invWorld.pointer(), 16);

			// set the ambient light
			SColorf c = m_Object->getSmgr()->getAmbientLight();
			float ac[4];
			ac[0] = c.getRed();
			ac[1] = c.getBlue();
			ac[2] = c.getGreen();
			ac[3] = c.getAlpha();
			services->setVertexShaderConstant(m_AmbientLightID, ac, 4);

			// set the camera position
			vector3df p = m_Object->getSmgr()->getActiveCamera()->getPosition();
			float cp[4];
			p.getAs4Values(cp);
			services->setVertexShaderConstant(m_CameraPositionID, cp, 4);

			IGE_Object* theLight = m_Object->getLevel()->getObjectManager()->getObjectPointerByName("Light", true);
			if (theLight)
			{
				// set the camera position
				vector3df lp = theLight->getAbsolutePosition();
				float l[4];
				lp.getAs4Values(l);
				services->setVertexShaderConstant(m_LightPositionID, l, 4);

				c = stringcToSColorf(theLight->getVariable("Diffuse"));
				ac[0] = c.getRed();
				ac[1] = c.getBlue();
				ac[2] = c.getGreen();
				ac[3] = c.getAlpha();
				//printf("Diffuse = %f %f %f %f\n", ac[0], ac[1], ac[2], ac[3]);
				services->setVertexShaderConstant(m_LightDiffuseID, ac, 4);

				c = stringcToSColorf(theLight->getVariable("Specular"));
				ac[0] = c.getRed();
				ac[1] = c.getBlue();
				ac[2] = c.getGreen();
				ac[3] = c.getAlpha();
				services->setVertexShaderConstant(m_LightSpecularID, ac, 4);

				// set the camera position
				vector3df at = stringcToVector3df(theLight->getVariable("Attenuation"));
				float att[4];
				at.getAs4Values(att);
				services->setVertexShaderConstant(m_LightAttenuationID, att, 4);
			}

			// set the mouse position
			float r[2];
			r[0] = (float)getObject()->getCursorControl()->getPosition().X;
			r[1] = (float)getObject()->getCursorControl()->getPosition().Y;
			services->setVertexShaderConstant(m_MousePositionID, (const irr::f32*)&r, 2);

			// set the time
			float time = (f32)m_Object->getDevice()->getTimer()->getTime() / 1000.0f;
			services->setVertexShaderConstant(m_TimeID, (const irr::f32*)&time, 1);
			//printf("time = %f\n", time);

			// set texture, for textures you can use both an int and a float setPixelShaderConstant interfaces (You need it only for an OpenGL driver).
			s32 TextureLayerID = 0;
			services->setPixelShaderConstant(m_Texture0ID, &TextureLayerID, 1);
			TextureLayerID = 1;
			services->setPixelShaderConstant(m_Texture1ID, &TextureLayerID, 2);
			TextureLayerID = 2;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 3);
			TextureLayerID = 3;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 4);
			TextureLayerID = 4;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 5);
			TextureLayerID = 5;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 6);
			TextureLayerID = 6;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 7);
			TextureLayerID = 7;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 8);
		}

	};
Last edited by Seven on Fri Jan 14, 2022 5:13 am, edited 1 time in total.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

the vertex shader

Code: Select all

#version 120

//////////////////////////////////////////////////////////////////////////////////
// uniforms are passed to the shader from the IShaderConstantSetCallBack
//////////////////////////////////////////////////////////////////////////////////
uniform mat4 mWorldViewProj;		// 
uniform mat4 mTransWorld;			//
uniform mat4 mInvWorld;				//
uniform vec4 mAmbientLight;			// ambient light value from IGE_Level instance
uniform vec4 mCameraPosition;		// current active camera position
uniform float mTime;				// time

// vertex uniforms for testing a single light 
uniform vec3 mLightPosition;		// a single light position
uniform vec4 mLightDiffuse;			// a single light 
uniform vec4 mLightSpecular;		// a single light 
uniform vec4 mLightAttenuation;		// a single light 

//////////////////////////////////////////////////////////////////////////////////
// varying values are set in the vertex shader but available in the pixel shader
// allows passing info from the vertex to the pixel shader
//////////////////////////////////////////////////////////////////////////////////
varying vec4	pAmbientLight;		// pass through to pixel shader
varying vec4	pCameraPosition;	// pass through to pixel shader
varying float	pTime;				// pass through to pixel shader

varying vec3	pLightPosition;		// pass through to pixel shader
varying vec4    pLightDiffuse;		// pass through to pixel shader
varying vec4    pLightSpecular;		// pass through to pixel shader
varying vec4    pLightAttenuation;	// pass through to pixel shader

// calculated in the vertex shader and passed to the pixel shader
varying vec4	pWorldPosition;		// vertex position in world coordinates
varying vec3	pNormal;			// the normal of the fragment			

void main (void)
{
	// remember these so that the pixel shader can access them if desired
 	pAmbientLight		= mAmbientLight;
    pCameraPosition		= mCameraPosition;
	pTime				= mTime;
	pNormal				= normalize(gl_Normal);
	pLightPosition		= mLightPosition;
	pLightDiffuse		= mLightDiffuse;
	pLightSpecular		= mLightSpecular;
	pLightAttenuation	= mLightAttenuation;

	// calculate these
	gl_Position		= mWorldViewProj * gl_Vertex;					// vertex position in screen coordinates?
	pWorldPosition	= gl_Vertex * mTransWorld;						// vertex position in world coordinates
	gl_FrontColor	= gl_BackColor = vec4(1.0f,1.0f,1.0f,1.0f);		// not sure why we do this here
	gl_TexCoord[0]	= gl_MultiTexCoord0;							// texture coord
}

// these are built in GLSL variables accissible in the pixel shader listed here as reference so i remember they are available if needed
/*
vec4 gl_FragCoord		read-only current fragment window relative coordinate. z is 0-1 and assigned to gl_FragDepth if no other value is assigned.
bool gl_FrontFacing		true if the fragment belongs to a front facing geometric primitive.
int gl_PointCoord		fragment position within a point (point rasterization only)in the range 0.0 to 1.0
vec4 gl_FragColor		the colour of the fragment
vec4 gl_FragData		data associated with the fragment
*/
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

the pixel shader

Code: Select all

#version 120

///////////////////////////////////////////////////////////////////////////////////
// bind all 8 textures in case we want to use them
// how to access the textures vec4 col0 = texture2D(Texture0, vec2(gl_TexCoord[0]));
///////////////////////////////////////////////////////////////////////////////////
uniform sampler2D Texture0;
uniform sampler2D Texture1;
uniform sampler2D Texture2;
uniform sampler2D Texture3;
uniform sampler2D Texture4;
uniform sampler2D Texture5;
uniform sampler2D Texture6;
uniform sampler2D Texture7;

//////////////////////////////////////////////////////////////////////////////////
// varying values are set in the vertex shader but available in the pixel shader
// allows passing info from the vertex to the pixel shader
//////////////////////////////////////////////////////////////////////////////////
varying vec4	pAmbientLight;		// pass through to pixel shader
varying vec4	pCameraPosition;	// pass through to pixel shader
varying float	pTime;				// pass through to pixel shader
varying vec3	pLightPosition;		// pass through to pixel shader
varying vec4    pLightDiffuse;		// pass through to pixel shader
varying vec4    pLightSpecular;		// pass through to pixel shader
varying vec4    pLightAttenuation;	// pass through to pixel shader
varying vec4	pWorldPosition;		// vertex position in world coordinates
varying vec3	pNormal;			// the normal of the fragment			

// the program entry point
void main()
{
	// temporary variables
	vec4 diffuse;
	vec4 lightColor;

	// get the direction from the fragment to the light, normalize it and then get the distance between them
	vec3 lDir = vec3(pLightPosition.xyz - pWorldPosition.xyz);
	lDir = normalize(lDir);
	float distance = length(lDir);

	// simple attenuation formula to approximate light drop off over distance
	float Attenuation = 1.0 - (pLightAttenuation.x+pLightAttenuation.y*distance+pLightAttenuation.z*distance*distance);
	
	// the diffuse color should be falloff value * the light color * ambient light 
	diffuse += dot(lDir, pNormal) * Attenuation * pLightDiffuse * pAmbientLight;

	// add in some textures for testing
	vec4 tex0 = texture2D(Texture0,  gl_TexCoord[0].st);
	vec4 tex1 = texture2D(Texture1,  gl_TexCoord[0].st);

	gl_FragColor = diffuse  * tex0 * tex1;
}
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

the problem is that the cube is black with no lighting.
when I print out the available getVertexShaderConstantID variables i get only these.

shader constants start
0 Texture0
1 Texture1
2 mAmbientLight
3 mLightAttenuation
4 mLightDiffuse
5 mLightPosition
6 mTransWorld
7 mWorldViewProj
shader constants end

meaning that my calls to
m_LightPositionID = services->getVertexShaderConstantID("mLightPosition");
m_LightDiffuseID = services->getVertexShaderConstantID("mLightDiffuse");
m_LightSpecularID = services->getVertexShaderConstantID("mLightSpecular");
m_LightAttenuationID = services->getVertexShaderConstantID("mLightAttenuation");
among others fails and returns with an index of -1


I am slowly losing my mind on this so any help would be appreciated. not so much in whether the shader code is optimized and whatnot, but in why the variables declared in the vertex shader are not visible to the services instance. when I look through the irrlicht code, i eventually wind up in the core opengl code (opengl loaded by pointer). researching that online tells me that the function calls by irrlicht should return the count of variables available and fill the passed in array with the variable names. my assumption is that the shader loader is 'stripping' the variables out but i dont know how to fix that and i dont now why it would when the variables are used in the shaders.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

sigh. suddenly all of the variables except for the pNormal are being listed. this is very frustrating.......

are the compiled shaders cached somewhere and I need to ensure that they are rebuilt?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: basic shader constant passing

Post by CuteAlien »

No immediate idea. Can look at it later mabye, but would be nice if you could also add some code to test the shader itself. And I see some things like IGE_VARIABLE_SETGET on a quick look which I don't know what they are about.
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
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

sorry, I should have detailed that macro. it simply creates a variable with setter/getter functions to save me some typing :)

#define IGE_VARIABLE_SETGET(type,nametag,value) private: type m_##nametag = value; public: virtual void set##nametag(type v) { m_##nametag = v;} virtual type get##nametag() { return m_##nametag;}
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

another option might be to forget my spaghetti code above completely. it is just a test bed for me to play with the shaders a little bit.

would anyone have a simple, single light shader that I can play around with? One that already works with irrlicht?

I am enjoying the shader code and have quite a few little effects running (one great example is a healthbar from Freya Holmer) however, none of these require having a normal available like a lighting shader does and that is where I keep getting stuck.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

with a little google help I have the following working as expected. now to learn passing multiple lights and material parameters.

shader callback

Code: Select all

#pragma once
#include "_IGE_Defines.h"
#include "IGE_Utils.h"
#include "IGE_Logger.h"
#include "IGE_Level.h"
#include "ige_string.h"
#include "IGE_Object.h"
#include "IGE_ObjectManager.h"

namespace IGE
{
	typedef class IGE_Application IGE_Application;
	typedef class IGE_ShaderManager IGE_ShaderManager;
	typedef class IGE_Level IGE_Level;

	class IGE_ShaderCallback : public IShaderConstantSetCallBack
	{
	private:
		IGE_VARIABLE_SETGET(bool, FirstUpdate, true);
		IGE_VARIABLE_GET(IGE_Object*, Object, nullptr);

		// vertex constants
		IGE_VARIABLE_SETGET(s32, WorldViewProjID, -1);
		IGE_VARIABLE_SETGET(s32, TransWorldID, -1);
		IGE_VARIABLE_SETGET(s32, InvWorldID, -1);
		IGE_VARIABLE_SETGET(s32, AmbientLightID, -1);
		IGE_VARIABLE_SETGET(s32, CameraPositionID, -1);
		IGE_VARIABLE_SETGET(s32, TimeID, -1);

		IGE_VARIABLE_SETGET(s32, LightPositionID, -1);
		IGE_VARIABLE_SETGET(s32, LightDiffuseID, -1);
		IGE_VARIABLE_SETGET(s32, LightSpecularID, -1);
		IGE_VARIABLE_SETGET(s32, LightAttenuationID, -1);

		// fragment constants
		IGE_VARIABLE_SETGET(s32, Texture0ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture1ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture2ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture3ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture4ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture5ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture6ID, -1);
		IGE_VARIABLE_SETGET(s32, Texture7ID, -1);

	public:
		IGE_ShaderCallback(IGE_Object* obj) : IShaderConstantSetCallBack(), m_Object(obj) { }

		virtual stringc getDirectory(stringc dirname) { return m_Object == nullptr ? IGE_DEFAULT_STRING : m_Object->getDirectory(dirname); }

		virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
		{
			// get shader constants id.
			if (m_FirstUpdate)
			{
				m_WorldViewProjID = services->getVertexShaderConstantID("mWorldViewProj");
				m_TransWorldID = services->getVertexShaderConstantID("mTransWorld");
				m_InvWorldID = services->getVertexShaderConstantID("mInvWorld");

				m_AmbientLightID = services->getVertexShaderConstantID("mAmbientLight");
				m_CameraPositionID = services->getVertexShaderConstantID("mCameraPosition");
				m_TimeID = services->getVertexShaderConstantID("mTime");

				m_LightPositionID = services->getVertexShaderConstantID("mLightPosition");
				m_LightDiffuseID = services->getVertexShaderConstantID("mLightDiffuse");
				m_LightSpecularID = services->getVertexShaderConstantID("mLightSpecular");
				m_LightAttenuationID = services->getVertexShaderConstantID("mLightAttenuation");

				m_Texture0ID = services->getPixelShaderConstantID("Texture0");
				m_Texture1ID = services->getPixelShaderConstantID("Texture1");
				m_Texture2ID = services->getPixelShaderConstantID("Texture2");
				m_Texture3ID = services->getPixelShaderConstantID("Texture3");
				m_Texture4ID = services->getPixelShaderConstantID("Texture4");
				m_Texture5ID = services->getPixelShaderConstantID("Texture5");
				m_Texture6ID = services->getPixelShaderConstantID("Texture6");
				m_Texture7ID = services->getPixelShaderConstantID("Texture7");
		
			}

			// vertex shader constants
			// set inverted world matrix
			video::IVideoDriver* driver = services->getVideoDriver();

			// set clip matrix
			core::matrix4 worldViewProj;
			worldViewProj = driver->getTransform(video::ETS_PROJECTION);
			worldViewProj *= driver->getTransform(video::ETS_VIEW);
			worldViewProj *= driver->getTransform(video::ETS_WORLD);

			services->setVertexShaderConstant(m_WorldViewProjID, worldViewProj.pointer(), 16);

			// set world matrixes
			core::matrix4 world = driver->getTransform(video::ETS_WORLD);
			world = world.getTransposed();
			services->setVertexShaderConstant(m_TransWorldID, world.pointer(), 16);

			core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
			invWorld.makeInverse();
			services->setVertexShaderConstant(m_InvWorldID, invWorld.pointer(), 16);

			// set the ambient light
			SColorf c = m_Object->getSmgr()->getAmbientLight();
			services->setVertexShaderConstant(m_AmbientLightID, reinterpret_cast<f32*>(&c), 4);

			// set the camera position
			vector3df p = m_Object->getSmgr()->getActiveCamera()->getPosition();
			float cp[4];
			p.getAs4Values(cp);
			services->setVertexShaderConstant(m_CameraPositionID, cp, 4);

			IGE_Object* theLight = m_Object->getLevel()->getObjectManager()->getObjectPointerByName("Light", true);
			if (theLight)
			{
				// set the camera position
				vector3df lp = theLight->getAbsolutePosition();
				float l[4];
				lp.getAs4Values(l);
				services->setVertexShaderConstant(m_LightPositionID, l, 4);

				c = stringcToSColorf(theLight->getVariable("Diffuse"));
				services->setVertexShaderConstant(m_LightDiffuseID, reinterpret_cast<f32*>(&c), 4);

				c = stringcToSColorf(theLight->getVariable("Specular"));
				services->setVertexShaderConstant(m_LightSpecularID, reinterpret_cast<f32*>(&c), 4);

				// set the camera position
				vector3df at = stringcToVector3df(theLight->getVariable("Attenuation"));
				at.normalize();
				float att[4];
				at.getAs4Values(att);
				services->setVertexShaderConstant(m_LightAttenuationID, att, 4);
			}

			// set the time
			float time = (f32)m_Object->getDevice()->getTimer()->getTime() / 1000.0f;
			services->setVertexShaderConstant(m_TimeID, (const irr::f32*)&time, 1);
			//printf("time = %f\n", time);

			// set texture, for textures you can use both an int and a float setPixelShaderConstant interfaces (You need it only for an OpenGL driver).
			s32 TextureLayerID = 0;
			services->setPixelShaderConstant(m_Texture0ID, &TextureLayerID, 1);
			TextureLayerID = 1;
			services->setPixelShaderConstant(m_Texture1ID, &TextureLayerID, 2);
			TextureLayerID = 2;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 3);
			TextureLayerID = 3;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 4);
			TextureLayerID = 4;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 5);
			TextureLayerID = 5;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 6);
			TextureLayerID = 6;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 7);
			TextureLayerID = 7;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 8);
		}

	};

	class IGE_Shader
	{
	private:
		IGE_VARIABLE_GET(IGE_ShaderManager*, ShaderManager, nullptr);
		IGE_VARIABLE_SETGET(IGE_ShaderCallback*, ShaderCallback, nullptr);
		IGE_VARIABLE_SETGET(stringc, Name, IGE_DEFAULT_STRING);
		IGE_VARIABLE_SETGET(E_MATERIAL_TYPE, MaterialType, EMT_SOLID);
		IGE_VARIABLE_SETGET(IGE_Object*, Object, nullptr);

	public:
		stringc getDirectory(stringc dirName);
	public:
		IGE_Shader(IGE_Object* obj, stringc name, IGE_ShaderManager* shadermanager, IGE_ShaderCallback* cb);
		virtual ~IGE_Shader();
	};

} // end namespace




vertex shader

Code: Select all

#version 120

//////////////////////////////////////////////////////////////////////////////////
// uniforms are passed to the shader from the IShaderConstantSetCallBack
//////////////////////////////////////////////////////////////////////////////////
uniform mat4 mWorldViewProj;		// 
uniform mat4 mTransWorld;			//
uniform mat4 mInvWorld;				//
uniform vec4 mAmbientLight;			// ambient light value from IGE_Level instance
uniform vec4 mCameraPosition;		// current active camera position
uniform float mTime;				// time

// vertex uniforms for testing a single light 
uniform vec3 mLightPosition;		// a single light position
uniform vec4 mLightDiffuse;			// a single light 
uniform vec4 mLightSpecular;		// a single light 
uniform vec4 mLightAttenuation;		// a single light 

//////////////////////////////////////////////////////////////////////////////////
// varying values are set in the vertex shader but available in the pixel shader
// allows passing info from the vertex to the pixel shader
//////////////////////////////////////////////////////////////////////////////////
varying vec3	pNormal;			// the normal of the fragment			
varying vec4	pAmbientLight;		// pass through to pixel shader
varying vec4	pCameraPosition;	// pass through to pixel shader
varying float	pTime;				// pass through to pixel shader

varying vec3	pLightPosition;		// pass through to pixel shader
varying vec4    pLightDiffuse;		// pass through to pixel shader
varying vec4    pLightSpecular;		// pass through to pixel shader
varying vec4    pLightAttenuation;	// pass through to pixel shader

// calculated in the vertex shader and passed to the pixel shader
varying vec4	pWorldPosition;		// vertex position in world coordinates

void main (void)
{
	// remember these so that the pixel shader can access them if desired
 	pAmbientLight		= mAmbientLight;
    pCameraPosition		= mCameraPosition;
	pTime				= mTime;
	
	pNormal	= normalize(gl_Normal);

	pLightPosition		= mLightPosition;
	pLightDiffuse		= mLightDiffuse;
	pLightSpecular		= mLightSpecular;
	pLightAttenuation	= mLightAttenuation;

	// calculate these
	gl_Position		= mWorldViewProj * gl_Vertex;					// vertex position in screen coordinates?
	pWorldPosition	= gl_Vertex * mTransWorld;						// vertex position in world coordinates
	gl_FrontColor	= gl_BackColor = vec4(1.0f,1.0f,1.0f,1.0f);		// not sure why we do this here
	gl_TexCoord[0]	= gl_MultiTexCoord0;							// texture coord
}

// these are built in GLSL variables accessible in the pixel shader listed here as reference so i remember they are available if needed
/*
vec4 gl_FragCoord		read-only current fragment window relative coordinate. z is 0-1 and assigned to gl_FragDepth if no other value is assigned.
bool gl_FrontFacing		true if the fragment belongs to a front facing geometric primitive.
int gl_PointCoord		fragment position within a point (point rasterization only)in the range 0.0 to 1.0
vec4 gl_FragColor		the colour of the fragment
vec4 gl_FragData		data associated with the fragment
*/
pixel shader

Code: Select all

#version 120

///////////////////////////////////////////////////////////////////////////////////
// bind all 8 textures in case we want to use them
// how to access the textures vec4 col0 = texture2D(Texture0, vec2(gl_TexCoord[0]));
///////////////////////////////////////////////////////////////////////////////////
uniform sampler2D Texture0;
uniform sampler2D Texture1;
uniform sampler2D Texture2;
uniform sampler2D Texture3;
uniform sampler2D Texture4;
uniform sampler2D Texture5;
uniform sampler2D Texture6;
uniform sampler2D Texture7;

//////////////////////////////////////////////////////////////////////////////////
// varying values are set in the vertex shader but available in the pixel shader
// allows passing info from the vertex to the pixel shader
//////////////////////////////////////////////////////////////////////////////////
varying vec4	pAmbientLight;		// pass through to pixel shader
varying vec4	pCameraPosition;	// pass through to pixel shader
varying float	pTime;				// pass through to pixel shader
varying vec3	pLightPosition;		// pass through to pixel shader
varying vec4    pLightDiffuse;		// pass through to pixel shader
varying vec4    pLightSpecular;		// pass through to pixel shader
varying vec4    pLightAttenuation;	// pass through to pixel shader
varying vec4	pWorldPosition;		// vertex position in world coordinates
varying vec3	pNormal;			// the normal of the fragment			

vec4 pointLight( vec4 pos, vec3 nrm, vec3 light, vec4 lcolor )
{
	vec4 diffuse;

	// get the direction from the fragment to the light, normalize it and then get the distance between them
	vec3 lDir = vec3(light.xyz - pos.xyz);
	lDir = normalize(lDir);
	float distance = length(lDir);

	// simple attenuation formula to approximate light drop off over distance
	float Attenuation = pLightAttenuation.x + pLightAttenuation.y*distance + pLightAttenuation.z*distance*distance;
	
	// the diffuse color should be falloff value * the light color * ambient light 
	diffuse += dot(lDir, nrm) * Attenuation * lcolor;
	diffuse.a = .0;
	return diffuse;
}

// the program entry point
void main()
{
	// grab the textur color
	vec4 tex0 = texture2D(Texture0,  gl_TexCoord[0].st);

	// mix in the lighting
	vec4 finalcolor = tex0 * pointLight(pWorldPosition, pNormal, pLightPosition, pLightDiffuse);

	// output the final color
	gl_FragColor = finalcolor;
}
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: basic shader constant passing

Post by CuteAlien »

Sorry, I didn't get to checking it this weekend. But sounds like it worked out anyway.

Lights are simpler - because they are not per material. So you generally can pass them in OnSetConstants.

Material is a bit of a problem in Irrlicht unfortunately. Because Irrlicht just has this limited SMaterial struct and most of the variables in there are used to set up render states. And you can only access the material in OnSetMaterial in the shader callback. But - that is only called when SMaterial has changed (checking operator!= of SMaterial). So passing additional per material info is tricky :-(
My usual trick is to just use the variables in SMaterial which are not used otherwise in the shader to pass my stuff.
Which variable is or isn't used is again depending on the base-material used for the shader and I'm still struggling with those parts myself.
MaterialTypeParam2 is generally free for example. And often you don't need all colors and can put stuff in there.

This is in urgent need of a rewrite a long time, but I never felt comfortable enough to work on it. I could probably add some more variables (for example for roughness and other typical modern lighting stuff) in there. Although each additional variable makes SMaterial comparisons more expensive and that is called a lot (well once per meshbuffer at least each frame). Still a few more check would likely not be too expensive and I simply haven't added more variables yet because I got away with it so far. And I hoped some day I'll figure out a better solution or something. Anyway... use what's there - and if it's not enough variable you can recommend some and maybe I'll just add them to trunk as long as they make sense (maybe UserData + a bool to force an OnSetMaterial call would be enough - the rest users could do with a map)

And I suspect (but never tried it), that another way might be to set shader constants by using custom scene nodes and setting them from there.

edit: For unchanging (or rarely changing) per material data you can also always use textures.
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
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: basic shader constant passing

Post by Seven »

thanks for the input. A lot for me to digest with shaders but it is moving along nicely.
Post Reply