A simple shader

Published September 26, 2016
Advertisement

After I expoted my obj object I still had one problem, irrLicht, for some reason, did not mix a directional and ambient light. Maybe because of the obj format, or maybe because I needed to give it a proper material? I am not sure, but I decided that by writing a simple shader I will easily fix that problem, and will have more control over the light. What I needed is a shader that can take a single directional light, apply it to the texture color and mix in ambient. I went with High-Level Shading Language (HLSL) as it is easy to understand and use, and it is kind of the present and the future.

I took an example shader from irrLicht, reworked it a little bit and made it do what I need. Here it is://-----------------------------------------------------------------------------// Global variables//-----------------------------------------------------------------------------float4x4 mWorldViewProj; // World * View * Projection transformationfloat4x4 mInvWorld; // Inverted world matrixfloat4x4 mTransWorld; // Transposed world matrixfloat3 mLightPos; // Light positionfloat4 mLightColor; // Light colorfloat4 mAmbientLight; // Light color// Vertex shader output structurestruct VS_OUTPUT{ float4 Position : POSITION; // vertex position float4 Diffuse : COLOR0; // vertex diffuse color float2 TexCoord : TEXCOORD0; // tex coords};VS_OUTPUT vertexMain(in float4 vPosition : POSITION, in float3 vNormal : NORMAL, float2 texCoord : TEXCOORD0 ){ VS_OUTPUT Output; // transform position to clip space Output.Position = mul(vPosition, mWorldViewProj); // transform normal float3 normal = mul(float4(vNormal,0.0), mInvWorld); // renormalize normal normal = normalize(normal); // position in world coodinates float3 worldpos = mul(mTransWorld, vPosition); // calculate light vector, vtxpos - lightpos float3 lightVector = worldpos - mLightPos; // normalize light vector lightVector = normalize(lightVector); // calculate light color float tmp = max(0, dot(-lightVector, normal)); float4 endColor = mLightColor * tmp; Output.Diffuse = endColor; Output.TexCoord = texCoord; return Output;}// Pixel shader output structurestruct PS_OUTPUT{ float4 RGBColor : COLOR0; // Pixel color};sampler2D myTexture; PS_OUTPUT pixelMain(float2 TexCoord : TEXCOORD0, float4 Position : POSITION, float4 Diffuse : COLOR0 ) { PS_OUTPUT Output; float4 col = tex2D( myTexture, TexCoord ); // sample color map // multiply with diffuse Output.RGBColor = col * (Diffuse * (float4(1.0, 1.0, 1.0, 0.0) - mAmbientLight) + mAmbientLight); //Output.RGBColor *= 7.0; return Output;}
The vertex pass (vertexMain) sets the positions, calculated the light color and sets it into defuse output. The pixel path (pixelMain) samples the texture for the pixel's color and mixes in diffuse and ambient lights/colors. Simple and easy.Output.RGBColor = col * (Diffuse * (float4(1.0, 1.0, 1.0, 0.0) - mAmbientLight) + mAmbientLight);
This line looks strange because I tried to make the shading break little it softer. I think it does it, I think I see a little bit of difference. But simple multiplication works fines too.


IrrLitch got a very simple way to handle shaders using video::IGPUProgrammingServices and addHighLevelShaderMaterialFromFiles function. To setup shader's global variables a callback class (video::IShaderConstantSetCallBack) is needed. Here is one I wrote, again taken from irrLitch shaders example: BasicShaderCallBack::BasicShaderCallBack(IrrlichtDevice* d, ILightSceneNode* l) { device = d; light = l; } void BasicShaderCallBack::OnSetConstants(video::IMaterialRendererServices* services, s32 userData) { video::IVideoDriver* driver = services->getVideoDriver(); // set inverted world matrix // if we are using highlevel shaders (the user can select this when // starting the program), we must set the constants by name. core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD); invWorld.makeInverse(); services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16); // 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("mWorldViewProj", worldViewProj.pointer(), 16); // set camera position core::vector3df pos = light->getAbsolutePosition(); services->setVertexShaderConstant("mLightPos", reinterpret_cast(&pos), 3); // set light color video::SColorf col(1.0f, 1.0f, 1.0f, 0.0f); services->setVertexShaderConstant("mLightColor", reinterpret_cast(&col), 4); // ste ambient light video::SColorf colAmbient(0.1f, 0.1f, 0.1f, 1.0f); services->setPixelShaderConstant("mAmbientLight", reinterpret_cast(&colAmbient), 4); // set transposed world matrix core::matrix4 world = driver->getTransform(video::ETS_WORLD); world = world.getTransposed(); services->setVertexShaderConstant("mTransWorld", world.pointer(), 16); // 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("myTexture", &TextureLayerID, 1); }
It takes a single light (ILightSceneNode) and device (IrrlichtDevice) to work out the color and positioning. It setups the direction light position, its color, ambient color, world matrices and actual texture. Colors are hardcoded for now, I will make it data driven later when I will setup proper lights.


Now in actual game code I do following: //directional light scene::ILightSceneNode* light2 = m_smgr->addLightSceneNode(0, core::vector3df(0, 0, 0), video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 100.0f); light2->setDebugDataVisible(scene::EDS_NORMALS); io::path hsFileName = "d3d9.hlsl"; // check for shader compatability if (!m_driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) && !m_driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)) { m_device->getLogger()->log("WARNING: Pixel shaders disabled "\ "because of missing driver/hardware support."); hsFileName = ""; } if (!m_driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) && !m_driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)) { m_device->getLogger()->log("WARNING: Vertex shaders disabled "\ "because of missing driver/hardware support."); hsFileName = ""; } //grab gpu video::IGPUProgrammingServices* gpu = m_driver->getGPUProgrammingServices(); s32 newMaterialType1 = 0; if (gpu) { BasicShaderCallBack* mc = new BasicShaderCallBack(m_device, light2); // create material from high level shaders (hlsl, glsl or cg) newMaterialType1 = gpu->addHighLevelShaderMaterialFromFiles( hsFileName, "vertexMain", video::EVST_VS_1_1, hsFileName, "pixelMain", video::EPST_PS_1_1, mc, video::EMT_SOLID, 0, video::EGSL_DEFAULT); } scene::IAnimatedMesh* mesh = m_smgr->getMesh("chr_old.obj"); IMeshSceneNode* node = m_smgr->addMeshSceneNode(mesh); if (node) { node->setMaterialTexture(0, m_driver->getTexture("chr_old.png")); node->setScale(vector3df(0.2f, 0.2f, 0.2f)); node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1); node->setDebugDataVisible(scene::EDS_NORMALS); }
Two important lines are: BasicShaderCallBack* mc = new BasicShaderCallBack(m_device, light2); // create material from high level shaders (hlsl, glsl or cg) newMaterialType1 = gpu->addHighLevelShaderMaterialFromFiles( hsFileName, "vertexMain", video::EVST_VS_1_1, hsFileName, "pixelMain", video::EPST_PS_1_1, mc, video::EMT_SOLID, 0, video::EGSL_DEFAULT);
Creating of actual material to use. And:node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1);
Set the material to a node. Easy and straight forward. Of course it is all good for simple testing, in a proper setup it will be a little bit more flexible and complicated. Shader will need to be able to take multiple lights and maybe do some effect like specular reflection. The output of all of that is:


shader.png

Looks nice and just the way I want it.

Sorry for slow updates, life been a little bit busy lately. Next I will try to work on a simple scene setup and maybe cover some of the Spark systems.

2 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement