Shader Base Material

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

Shader Base Material

Post by Seven »

When using my own shader, is is accurate to think that I could use only the EMT_SOLID or EMT_TRANSPARENT_ADD_COLOR(or variation of transparent) as the 'base' material in order to have the system sort / render correctly?

for example, using EMT_DETAIL_MAP would be the same as using EMT_SOLID and would be in the non-transparent rendering pass where as the EMT_TRANSPARENT_ADD_COLOR would give the same output (since the shader outputs all of the pixels) but would be in the transparent pass for rendering performing transparency? (simply set the alpha of the pixel to 0)

or do i have to clip pixels in the shader to get transparency?
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Shader Base Material

Post by CuteAlien »

Phew, one of the confusing parts...

In Irrlicht trunk this has been unified so base material affects 4 variables in COpenGLSLMaterialRenderer. And aside from those nothing should really be different anymore (Irrlicht 1.9 was very different, this is our first step in getting toward shader-materials...). Those are Alpha, FixedBlending and Blending and AlphaTest. And they are set like this:

Code: Select all

case EMT_TRANSPARENT_VERTEX_ALPHA:
case EMT_TRANSPARENT_ALPHA_CHANNEL:
case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
	Alpha = true;
	break;
case EMT_TRANSPARENT_ADD_COLOR:
case EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR:
case EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR:
	FixedBlending = true;
	break;
case EMT_ONETEXTURE_BLEND:
	Blending = true;
	break;
case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
	AlphaTest = true;
	break;
default:
   AlphaTest = false; Alpha = false; FixedBlending = false; Blending = false;
So that's the part that really matter from base-materials. And you are right - EMT_DETAIL_MAP and EMT_SOLID are the same in Irrlicht trunk.

What they are (I think... struggling a bit myself each time):
Alpha: Enables blending with blend function GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
FixedBlending: Enables blending with blend function GL_ONE, GL_ONE_MINUS_SRC_COLOR
Blending: Enables blending with function parameters from SMaterial:: MaterialTypeParam which you can set with pack_textureBlendFunc
AlphaTest: No real blending, but some sharp color clipping. Transparent when alpha < 128, otherwise solid.

edit: I mentioned OpenGL names here - D3D should be similar.
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
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Shader Base Material

Post by CuteAlien »

Ah right. Not enough confusion. This was about setting the variables for the low-level drivers.
But when it comes to transparency there are certainly 2 more things to care about.

First is z-write handling:
For shaders z-writing is enabled as soon as Alpha, FixedBlender or Blending are used OR SMaterial::isAlphaBlendOperation is true (it uses needsTransparentRenderPass function to decide).

Second is that transparent nodes are sorted back to front when using the scenemanager for rendering. Which happens as soon as a node is in one of the transparent render passes. Where it is put either by using registerNodeForRendering with ESNRP_TRANSPARENT or ESNRP_TRANSPARENT_EFFECT. Or (and that's the usual case) with ESNRP_AUTOMATIC and then needsTransparentRenderPass is checked again for the material used in that pass. Note that you kinda get another sorting from renderpasses themself - as they draw in a certain order (like ESNRP_TRANSPARENT before ESNRP_TRANSPARENT_EFFECT). In theory each node-type decides itself where it registers itself, but in Irrlicht trunk they should by now all use IDriver::needsTransparentRenderPass for this decision (in Irrlicht 1.9 it was a bit of a mess).

And minor note: there is no per polygon sorting so far. One day...
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: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Shader Base Material

Post by Seven »

perfect. I solved my issue. thanks for the help.
are there any examples of using multiple shaders on one scenenode?
last but not least, is the overridematerial useful for something like when selecting an object it would be nice to have a shader that runs to show it selected. (i currently use debuginfo but would rather have my own shader run.
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Shader Base Material

Post by CuteAlien »

You mean switching shaders? No examples probably. Depends a bit on the situation what I do. Sometimes I push all node materials to a stack - change materials - render - pop old node materials back.
If not all nodes are involved then I might remember nodes and material-indices involved in my stack in some struct, like:

Code: Select all

struct SMaterialBackup
{
	SMaterialBackup(const irr::video::SMaterial& m, irr::u32 idx, irr::scene::ISceneNode* n)
		: material(m), materialIdx(idx), node(n)
	{}

	irr::video::SMaterial material; 
	irr::u32 materialIdx;
	irr::scene::ISceneNode* node;
};
std::vector<SMaterialBackup> backupMaterials;
So let's say I want to backup all nodes involved in shadows it's:

Code: Select all

for ( auto node : shadowNodeArray )
{
	for ( irr::u32 i=0; i< node->getMaterialCount(); ++i )
		backupMaterials.push_back(SMaterialBackup(node->getMaterial(i), i, node));
}

// then modify materials in some way before rendering

smgr->drawAll();

// restore materials
for ( auto& backup : backupMaterials )
{
	backup.node->getMaterial(backup.materialIdx) = backup.material;
}
More ways to do it I guess.

Needing more variables to pass to shader than SMaterial offers is unfortunately always pretty hard.
For stuff like highlighting I generally try to find some unused variable in the material and abuse it. MaterialTypeParam is often unused. Even wrote me some helper struct where I can use floats bytewise in case I need a few more bytes.

Code: Select all

union UMaterialTypeParam
{
	UMaterialTypeParam(irr::f32 f) : value(f) {}

	irr::f32 value;
	struct 
	{
		irr::u8 reserved1;
		irr::u8 reserved2;
		irr::u8 reserved3;
		EColHighlight highlight;
	};
};

// now this switches only one byte of MaterialTypeParam in a SMaterial variable called mat
UMaterialTypeParam mtp(mat.MaterialTypeParam); // initalize with old f32 value
materialTypeParam.highlight = xxx; // set the highlighting byte
mat.MaterialTypeParam = materialTypeParam.value; // now we got the change f32 value
I rarely need extra floats - so I get 4 bytes out of each float that way.
Also sometimes not all colors are needed etc.

Override material has other use-cases. Thought I have to admit not having more variables is annoying (there's some reason - as material switches tend to have some costs).
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: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Shader Base Material

Post by Seven »

seems we think alike. I am doing something similar to what you have but on a per object case. basically I have a shadermanager that creates shaders and then use node->setmaterialType() as needed inside the game objects. (curently have a base shadertype in each obejct but can set the current shader as anything, always reverting back tot he base shader when desired)

I might be confusing myself though, because I see that the scennodes have the ability to have multiple materials. I had assumed you could assign different sahders and the system would automatically make multiple renderpasses to apply each one in turn. I will have to study into that more. Not sure how that works since some functions seem to be entire scenenode

//! Sets the material type of all materials in this scene node to a new material type.
/** \param newType New type of material to be set. */
void setMaterialType(video::E_MATERIAL_TYPE newType)

but then we have a materialcount also....... I'll have to study it I think.....
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Shader Base Material

Post by Seven »

maybe I am doing it wrong. I dont use the material variables at all in the shader, I send them all through the onsetconstants() function.

for example , one of my lit shaders with 8 textures.
(the whole stringtoFloat() and such is just my own nonsense for having exposed variables for the scripting language)
(also, IGE_ADD_VARIABLE just prevents some typeing for me)
#define IGE_VARIABLE_SETGET(x,y,z) private: x m_##y = z; public : virtual void set##y(x v) { m_##y = v; } virtual x get##y() { return m_##y; }


if there is a better way to do this PLEASE teach me :)

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, 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);
		IGE_VARIABLE_SETGET(s32, LightRadiusID, -1);

		IGE_VARIABLE_SETGET(s32, FogColorID, -1);
		IGE_VARIABLE_SETGET(s32, FogStartID, -1);
		IGE_VARIABLE_SETGET(s32, FogEndID, -1);
		IGE_VARIABLE_SETGET(s32, FogDensityID, -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_LightRadiusID = services->getVertexShaderConstantID("mLightRadius");

				m_FogColorID = services->getVertexShaderConstantID("mFogColor");
				m_FogStartID = services->getVertexShaderConstantID("mFogStart");
				m_FogEndID = services->getVertexShaderConstantID("mFogEnd");
				m_FogDensityID = services->getVertexShaderConstantID("mFogDensity");

				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");

				//printf("texture IDs = %d %d %d %d %d\n", m_Texture0ID, m_Texture1ID, m_Texture2ID, m_Texture3ID, m_Texture4ID);
				return;
			}

			// 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);
			//printf("ambient light = %s\n", SColorfToStringc(c).c_str());

			SColor c2 = m_Object->getLevel()->getFogColor();
			SColorf c3(c2);
			services->setVertexShaderConstant(m_FogColorID, reinterpret_cast<f32*>(&c3), 4);

			float fs = m_Object->getLevel()->getFogStart();
			services->setVertexShaderConstant(m_FogStartID, reinterpret_cast<f32*>(&fs), 1);
			float fe = m_Object->getLevel()->getFogEnd();
			services->setVertexShaderConstant(m_FogEndID, reinterpret_cast<f32*>(&fe), 1);
			float fd = m_Object->getLevel()->getFogDensity();
			services->setVertexShaderConstant(m_FogDensityID, reinterpret_cast<f32*>(&fd), 1);

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

			vector3df lightPos = vector3df(0, 0, 0);
			SColorf lightDiffuse = SColorf(0, 0, 0, 0);
			SColorf lightSpecular = SColorf(0, 0, 0, 0);
			vector3df lightAttenuation = vector3df(0.03f, 0.03f, 0.03f);
			float lightRadius = 300;
			float l[4];
			float att[4];

			IGE_Object* theLight = m_Object->getLevel()->getObjectManager()->getObjectPointerByName("Light", true);
			if (theLight)
			{
				lightPos = theLight->getAbsolutePosition();
				lightDiffuse = stringcToSColorf(theLight->getVariable("Diffuse"));
				lightSpecular = stringcToSColorf(theLight->getVariable("Specular"));
				lightRadius = stringcToFloat(theLight->getVariable("Radius"));
				lightAttenuation = stringcToVector3df(theLight->getVariable("Attenuation"));
			}
			else
			{
				theLight = m_Object->getLevel()->getObjectManager()->getObjectPointerByName("IGEO_CAMERALIGHT", true);
				if (theLight)
				{
					lightPos = theLight->getAbsolutePosition();
					lightDiffuse = stringcToSColorf(theLight->getVariable("Diffuse"));
					lightSpecular = stringcToSColorf(theLight->getVariable("Specular"));
					lightRadius = stringcToFloat(theLight->getVariable("Radius"));
					lightAttenuation = stringcToVector3df(theLight->getVariable("Attenuation"));
				}
			}

			lightPos.getAs4Values(l);
			services->setVertexShaderConstant(m_LightPositionID, l, 4);
			services->setVertexShaderConstant(m_LightDiffuseID, reinterpret_cast<f32*>(&lightDiffuse), 4);
			services->setVertexShaderConstant(m_LightSpecularID, reinterpret_cast<f32*>(&lightSpecular), 4);
			services->setVertexShaderConstant(m_LightRadiusID, reinterpret_cast<f32*>(&lightRadius), 1);
			lightAttenuation.normalize();
			lightAttenuation.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, 1);
			TextureLayerID = 2;
			services->setPixelShaderConstant(m_Texture2ID, &TextureLayerID, 1);
			TextureLayerID = 3;
			services->setPixelShaderConstant(m_Texture3ID, &TextureLayerID, 1);
			TextureLayerID = 4;
			services->setPixelShaderConstant(m_Texture4ID, &TextureLayerID, 1);
			TextureLayerID = 5;
			services->setPixelShaderConstant(m_Texture5ID, &TextureLayerID, 1);
			TextureLayerID = 6;
			services->setPixelShaderConstant(m_Texture6ID, &TextureLayerID, 1);
			TextureLayerID = 7;
			services->setPixelShaderConstant(m_Texture7ID, &TextureLayerID, 1);
		}

	};

again, if there is a better way please feel free to teach me!

how would I access a material variable inside the shader?
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Shader Base Material

Post by Seven »

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 
uniform float mLightRadius;			// a single light 

uniform vec4 mFogColor;			// fog color 
uniform float mFogStart;		//  
uniform float mFogEnd;			//  
uniform float mFogDensity;		//  

//////////////////////////////////////////////////////////////////////////////////
// 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
varying float   pLightRadius;		// pass through to pixel shader

varying vec4    pFogColor;			// pass through to pixel shader
varying float   pFogStart;			// pass through to pixel shader
varying float   pFogEnd;			// pass through to pixel shader
varying float   pFogDensity;		// 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;
	pLightRadius		= mLightRadius;

	pFogColor = mFogColor;
	pFogStart = mFogStart;
	pFogEnd = mFogEnd;
	pFogDensity = mFogDensity;

	// 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
*/

Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Shader Base Material

Post by Seven »

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 float   pLightRadius;		// pass through to pixel shader
varying vec4	pWorldPosition;		// vertex position in world coordinates
varying vec3	pNormal;			// the normal of the fragment			
varying vec4    pFogColor;			// pass through to pixel shader
varying float   pFogStart;			// pass through to pixel shader
varying float   pFogEnd;			// pass through to pixel shader
varying float   pFogDensity;		// pass through to pixel shader

// FOG
float getFogFactor(float fogCoordinate)
{
	float result = 0.0;
	float fogLength = pFogEnd - pFogStart;
	result = (pFogEnd - fogCoordinate) / fogLength;
	result = 1.0 - clamp(result, 0.0, 1.0);
	return result;
}
/////


/// POINT LIGHT and AMBIENT
vec4 Light_Point( vec4 pos, vec3 nrm, vec3 light, vec4 lcolor, vec3 att )
{
	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);
	float distance = length(lDir);
	lDir = normalize(lDir);

	// simple attenuation formula to approximate light drop off over distance
	float intensity = 1 - ((distance - pLightRadius) / pLightRadius);

	// keep in the 0 to 1 range
	intensity = clamp(intensity,0.0f,1.0f);

	// the diffuse color should be falloff value * the light color * ambient light 
	diffuse += dot(lDir, nrm) * lcolor * intensity;

	diffuse += pAmbientLight;

	// light alpha is always 1
	diffuse.a = 1.0;

	// return the lighted pixel color
	return diffuse;
}

// the program entry point
void main()
{
	vec3 vNormalized = normalize(pNormal);
	
	vec4 vTexColor = vec4(0.0);
	vec2 vTexCoord = gl_TexCoord[0].st;
	vec2 vTexCoord2 = gl_TexCoord[0].st * 500;

	// path texture
	vec4 PathTexColor = texture2D(Texture5, vTexCoord);

	// default base colors mized
	vec4 TexColor0 = texture2D(Texture0, vTexCoord2) * .3;
	vec4 TexColor1 = texture2D(Texture1, vTexCoord2) * .3;

	// mixed rocks, grass etc based on path texture colors
	vec4 TexColor2 = texture2D(Texture2, vTexCoord2) * PathTexColor.r;
	vec4 TexColor3 = texture2D(Texture3, vTexCoord2) * PathTexColor.b;
	vec4 TexColor4 = texture2D(Texture4, vTexCoord2) * PathTexColor.g;
 
	// combine them all togther
	vec4 vFinalTexColor = TexColor2 + TexColor3+ TexColor4;

	// mix in the point lighting if desired
#if(1)
	vec4 pointlight =  Light_Point(pWorldPosition, pNormal, pLightPosition, pLightDiffuse, pLightAttenuation.xyz);
	vFinalTexColor = vFinalTexColor * pointlight;
#endif

#if(1)
	float fogCoordinate = length(pCameraPosition - pWorldPosition);
	vFinalTexColor = mix(vFinalTexColor, pFogColor, getFogFactor(fogCoordinate));
#endif

	clamp(vFinalTexColor.r,0.0f,1.0f);
	clamp(vFinalTexColor.g,0.0f,1.0f);
	clamp(vFinalTexColor.b,0.0f,1.0f);
	vFinalTexColor.a = 1.0f;


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

Re: Shader Base Material

Post by CuteAlien »

Seven wrote: Fri Aug 05, 2022 9:53 pm I might be confusing myself though, because I see that the scennodes have the ability to have multiple materials. I had assumed you could assign different sahders and the system would automatically make multiple renderpasses to apply each one in turn.
Nope, that's 1 material per meshbuffer. You can have different meshbuffers in a mesh - and each needs it's own material.
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
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Shader Base Material

Post by CuteAlien »

Accessing material variables in shaders: You copy them in OnSetMaterial to your shader class. Then in OnSetConstants you can pass them on.
The problem with OnSetConstants is that it's one for all nodes. Unless you create a shader-material per node. Or you figure out some way to access the node in OnSetConstants. Which I think is probably not possible right now (it's a bit tricky as that low-level it doesn't have the concept of nodes, but only drawCall with a material). Thought maybe SceneManager could have the concept of currently rendered node (not sure if it helps).
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: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Shader Base Material

Post by Seven »

gotcha. It seems we still think alike so I feel I am on the right track. I create a shader instance for each object and then store the object instance inside the shader class so that I have access to the scenenode (as well as the lights, fog etc... though the object and into the level instance). I will have to devise a method to reach each meshbuffer although that shouldnt be too difficult when I get to that level.

for now I want to create shaders for all material types so that I can start my deferred rendering studies. Once I have the forward rendering done using my own shaders, I should be able to switch between forward / deferred at runtime which is my final goal.
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Shader Base Material

Post by CuteAlien »

Hm, I never tried just creating one shader instance per object, so I can't really tell if that is a good idea or not. The constant material changes this causes might have some overhead? Just guessing - never tried it that way. In my irr-playground-micha repository (see sig) I got a few files called profile* which can be useful to test the effect stuff has on speed. Thought you probably have your own tests ;-)
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: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Shader Base Material

Post by Seven »

thanks for all the help! I now have things working as desired.

in the shader system I have, I currently have to paste the lighting and fog code into each shader. is it possible to use a #include statement inside the shaders for irrlicht so i can have a single lighting / fog file?
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Shader Base Material

Post by CuteAlien »

You need your own pre-processor for this. XEffects (https://github.com/monstrobishi/xeffect ... ter/Source) has one with CPreShader.cpp/.h . That one doesn't have include yet, but I've added a function for that which looks somewhat like following (not tested right now if I forgot something, but I think aside from STL it only uses getFileContent from those XEffect files ... which could probably be optimized, not sure why that reads stuff per line, but won't matter much).

Code: Select all

static void resolveIncludes(std::string& shaderProgram, const std::string& includePath)
{
	std::string::size_type includePos = 0;
	const std::string includeLabel("##include");
	while ((includePos = shaderProgram.find(includeLabel, includePos)) != std::string::npos)
	{
		std::string::size_type posStartStr = shaderProgram.find("\"", includePos + includeLabel.size());
		if (posStartStr == std::string::npos)
		{
			Logger::log("Shader preprocessor: ##include statement not followed by \".");
			return;
		}
		++posStartStr;
		std::string::size_type posEndStr = shaderProgram.find("\"", posStartStr);
		if (posEndStr == std::string::npos)
		{
			Logger::log("Shader preprocessor: ##include statement missing closing \".");
			return;
		}

		std::string includeFilename = includePath + shaderProgram.substr(posStartStr, posEndStr - posStartStr);
		std::string includeContent = getFileContent(includeFilename.c_str());
		if (includeContent.empty())
			Logger::log("Shader preprocessor: ##include returned empty string");	// Could be OK, but usually isn't. getFileContent will deliver errors.
		else
			stripComments(includeContent);

		shaderProgram = shaderProgram.substr(0, includePos) + includeContent + shaderProgram.substr(posEndStr + 1, std::string::npos);
		// Not skipping over includeContent as it might have further includes. Means: infinite recursive includes can happen (and can't be avoided with defines as those are handled later). Just don't make shader too complicated.
	}
}
Note that I kept the same style as XEffects - everything in our own pre-processor uses 2 ##, so for example ##include to distinguish it from the pre-processor which the shader compiler itself runs. Also as I run resolveIncludes it doesn't care about defines (so you can't define out the include).

Hm, I see I also rewrote comment-stripping a bit. Mainly to keep line-numbers intact, didn't like how it removed them as shader-compiler then gave wrong lines on errors. And only makes sense if you use rest of that pre-processor as it's just an easy way to prevent accidentally using commented out defines later:

Code: Select all

static void stripComments(std::string &shaderProgram)
{
	// Strip all comments, they get in the way.
	// Note they are replaced by whitespace which does not affect line-numbers.
	// (the reason error-line-numbers are still wrong sometimes is because of nvidia's compiler messing it up)
	std::string::size_type currentSearchPos = 0;

	// c++ style comments // 
	while ((currentSearchPos = shaderProgram.find("//", currentSearchPos)) != std::string::npos)
	{
		std::string::size_type findEnd = shaderProgram.find('\n', currentSearchPos+2);
		if (findEnd == std::string::npos)	// no eol, so go until eof
			findEnd = shaderProgram.size();
		for (size_t i = currentSearchPos; i < findEnd; ++i)
			shaderProgram[i] = ' ';
	}

	// c style comments /* */
	currentSearchPos = 0;
	while ((currentSearchPos = shaderProgram.find("/*", currentSearchPos)) != std::string::npos)
	{
		std::string::size_type findEnd = shaderProgram.find("*/", currentSearchPos+2);
		if (findEnd == std::string::npos)	// no end of commment - use eol
			findEnd = shaderProgram.size();
		else
			findEnd += 2;	// overwrite ending-chars as well 
		for (size_t i = currentSearchPos; i < findEnd; ++i)
			shaderProgram[i] = ' ';
	}
}
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