PDA

View Full Version : Trying to draw multiple objects with different shaders



ErickHawking
06-17-2017, 08:05 PM
Hello, I have a basic scene of a player (triangle) and a skybox. The skybox has its own shader program. Here is how I am attempting to draw both entities:



void Game::Render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Shader.UpdateCameraPos(Camera.GetCameraPosition()) ;

RenderSkybox();
RenderPlayer();
SDL_GL_SwapWindow(MainWindow);
}
void Game::RenderSkybox()
{
SkyboxShader.UpdateSkyboxTexture(m_SkyboxTextureID );
SkyboxShader.UpdateTransform(m_SkyboxTransform, m_Camera);
SkyboxShader.UseProgram();
Skybox.Draw();
}
void Game::RenderPlayer()
{
Shader.UseProgram();
Shader.UpdateTransform(m_Transformation, m_Camera);
Texture.BindTexture(0);
Player.Draw();
}

However, what's currently happening, the player is being drawn but the skybox is not. If I swap RenderSkybox() with RenderPlayer() like so:

void Game::Render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Shader.UpdateCameraPos(Camera.GetCameraPosition()) ;

RenderPlayer();
RenderSkybox();

SDL_GL_SwapWindow(m_MainWindow);
}

Then nothing gets rendered to the screen (the result is a black screen). Why does this happen?

Thanks for reading my thread.

ErickHawking
06-18-2017, 05:42 AM
Hello, I am trying to draw two objects with different shaders, a skybox and a player (triangle). But for some reason, one of the shaders sort of override each other and I don't know why... :confused:

Here is how I am rendering things:



void Game::RenderSkybox()
{
SkyboxShader.UpdateSkyboxTexture(SkyboxTextureID);
SkyboxShader.UpdateTransform(SkyboxTransform, Camera);
SkyboxShader.UseProgram();
Skybox.Draw();
}
void Game::RenderPlayer()
{
SimpleShader.UpdateTransform(Transformation, Camera);
SimpleShader.UseProgram();
Texture.BindTexture(0);
Player.Draw();
}

void Game::Render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

SimpleShader.UpdateCameraPos(Camera.GetCameraPosit ion());

RenderSkybox();
RenderPlayer();

SDL_GL_SwapWindow(MainWindow);
}

EDIT: So if I render in that order, I could see the skybox but the player is rendered in an incorrect manner (I think it is taking the scale from the skybox shader and applying it to the player...) and if I render the player before the skybox, then the player doesn't get rendered... What is causing this behavior to occur?

Thank you for reading my thread.

TheFearlessHobbit
06-18-2017, 06:12 AM
Hello Erick, I think I know what's going on with your program. The glUniform calls change the currently bound program's uniform state. So, for example, when you do this:



void Game::RenderSkybox()
{
SkyboxShader.UpdateSkyboxTexture(SkyboxTextureID);
SkyboxShader.UpdateTransform(SkyboxTransform, Camera);
SkyboxShader.UseProgram();
Skybox.Draw();
}
void Game::RenderPlayer()
{
SimpleShader.UpdateTransform(Transformation, Camera);
SimpleShader.UseProgram();
Texture.BindTexture(0);
Player.Draw();
}

When you're rendering the player, the transform will only affect the currently bound shader which is your skybox shader and not your simple shader. So whatever program passed to glUseProgram most recently. To fix this, you must switch shader programs before modifying any uniforms. Try this:



void Game::RenderSkybox()
{
SkyboxShader.UseProgram();
SkyboxShader.UpdateSkyboxTexture(SkyboxTextureID);
SkyboxShader.UpdateTransform(SkyboxTransform, Camera);
Skybox.Draw();
}
void Game::RenderPlayer()
{
SimpleShader.UseProgram();
SimpleShader.UpdateTransform(Transformation, Camera);
Texture.BindTexture(0);
Player.Draw();
}

Hope this helped.

ErickHawking
06-18-2017, 06:15 AM
Hello Erick, I think I know what's going on with your program. The glUniform calls change the currently bound program's uniform state. So, for example, when you do this:



void Game::RenderSkybox()
{
SkyboxShader.UpdateSkyboxTexture(SkyboxTextureID);
SkyboxShader.UpdateTransform(SkyboxTransform, Camera);
SkyboxShader.UseProgram();
Skybox.Draw();
}
void Game::RenderPlayer()
{
SimpleShader.UpdateTransform(Transformation, Camera);
SimpleShader.UseProgram();
Texture.BindTexture(0);
Player.Draw();
}

When you're rendering the player, the transform will only affect the currently bound shader which is your skybox shader and not your simple shader. So whatever program passed to glUseProgram most recently. To fix this, you must switch shader programs before modifying any uniforms. Try this:



void Game::RenderSkybox()
{
SkyboxShader.UseProgram();
SkyboxShader.UpdateSkyboxTexture(SkyboxTextureID);
SkyboxShader.UpdateTransform(SkyboxTransform, Camera);
Skybox.Draw();
}
void Game::RenderPlayer()
{
SimpleShader.UseProgram();
SimpleShader.UpdateTransform(Transformation, Camera);
Texture.BindTexture(0);
Player.Draw();
}

Hope this helped.

Oh wow really? This is such an important thing I'm surprised how many of the books and tutorials I've read didn't mention this... thank you for letting me know about this, it works now. :)

GClements
06-18-2017, 08:23 AM
Uniforms in the default uniform block (top-level variables in the shader, set with glUniform()) are part of the program object.

Named uniform blocks allow you to have uniform variables which are stored in a buffer object. This allows you to use the same variable state with multiple programs, or use a single program in multiple contexts with each instance having their own variables.