PDA

View Full Version : Passing Argument As Uniform Causes Different Shader To Fail



larryl
04-17-2017, 06:23 PM
Hi Folks:

I'm sorry for the length of this post. I wish I could tell you it's a well written and easy read. All I can say is if I had the skill to explain it in simpler terms, I would.

Developing on Windows 10, VS 2017, C++.

Thanks to a lot of generous advice from this forum, and guidance from this (https://learnopengl.com/#!Introduction) tutorial, I'm rendering my first OpenGL image, an early version of a splash screen:



Uh, "1_material_colors.jpg: This is not a valid image file". That's the same error that popped up when I tried to insert a PNG image.

How should I interpret this?

Add an Image from your computer
Allowed Filetypes: jpg, jpeg, png, gif


Does MS Paint not create valid PNG and JPG files?

Maybe I've used up my quota of dumb questions and I can't post images.

Ok, a word is worth a thousandth of a picture, so picture this:

Two words in bold Ariel Italic dominate the window, which has a black background. The first, in light blue green, says "Net" and below that is "Results" in blood read.

Rotating leisurely in front of this text is a simple model of a tennis court I made in Blender. It's drawn large enough to almost touch the right and left edges of the window. The view is across the court. The court and axis of rotation are tilted slightly so the playing surface is can be seen. The application's name is clearly visible, The lower word "Results" is briefly obscured for part of the court's rotation.

To my eyes my first OpenGL image is beautiful. The scene is vibrant, colors are straight from the materials defined in the model, unfaded by lighting effects.

I believe the technical term for such an image is "Crap".

Time to make it look like something from this world.

Here's how the code's supposed to work:

I started with two identical sets of shaders, one set for the text and another for the court.

In the code called by the Windows message loop I select the appropriate shader for each task.


#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 3) in vec4 color;

out vec4 vertex_Color;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
vertex_Color = color;
}

Here is the fragment shader:


#version 330 core

in vec4 vertex_Color;

out vec4 color;

void main()
{
vec4 fragment_color = vec4(vertex_Color);
if(fragment_color.a < 0.1)
discard;
color = fragment_color;
}

As I develop the image for the court its shaders will evolve.

I don't have plans for changing the text shaders. I still have 1/2 of the tutorial to grind through, so that could change if I decide to add effects to the text.

The Problem:

Ambient light is implemented by modifying the court's fragment shader with a uniform for the ambient color. The output color from the fragment shader is the material's color multiplied by the the ambient color.

Here's the court's fragment shader, after ambient light processing has been added:


#version 330 core

in vec4 vertex_Color;

out vec4 color;

uniform vec4 total_ambient_color;
// vec4 total_ambient_color = vec4(0.268582731, 0.382265419, 0.268573165, 1.0);

void main()
{
vec4 fragment_color = vec4(vertex_Color);
if(fragment_color.a < 0.1)
discard;
color = fragment_color * total_ambient_color;
}

I add the necessary uniform code to the application.

And there's the rotating court, dimmed appropriately. The court's shader worked.

The problem is that there's no text displayed, nothing. Just blackness.

Could be a million things, right?

What happens if I hard code the ambient color into the court's fragment shader?

You see the commented out "vec4 total_ambient_color = vec4(0.268582731..."?

Remove the comment "//", and comment out the uniform statement.

Run it again and there's the court, dimly rotating in front of the text, which is nice and bright. This is what I want to see.

So it looks like I'm messing up the uniform call, but I don't see what I'm doing wrong. At the end of this post, after the C++ setup and loop code, I compare it to similar code form the tutorial, looks the same to me.

Here is the code that set's up for the graphics:


bool setup_simple_splash_screen(HWND hdlg, HINSTANCE hinstance, PROCESS_RECORD *process_record_ptr,
std::string *message_string_ptr)
{
bool error_found = false;
GRAPHICS_PARAMETER *graphics_parameter_ptr = new GRAPHICS_PARAMETER();
windows_camera_wrapper_basic::CAMERA_WRAPPER *free_camera_wrapper_ptr = NULL;
opengl_camera::CAMERA *camera_ptr = NULL;
vector<Mesh> *meshes_vector_ptr = NULL;

HDC hdc = NULL;
std::string local_message_string;
RECT client_rect;
int width = 0;
int height = 0;

const int PixelFormatAttribList[] =
{
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, 32,
0 // End of attributes list
};

graphics_parameter_ptr->set_hwnd(hdlg);

graphics_parameter_ptr->set_hrc(init_opengl(hdlg, &hdc, hinstance, 3, 3,
PixelFormatAttribList,
&local_message_string));

if (graphics_parameter_ptr->get_hrc() == NULL)
{
error_found = true;

if (message_string_ptr != NULL)
{
*message_string_ptr += "setup_splash_screen()\n";
*message_string_ptr += " HRC is NULL.\n";
*message_string_ptr += local_message_string.c_str();
local_message_string.erase();
}
}
else
{
graphics_parameter_ptr->set_hdc(hdc);

if (graphics_parameter_ptr->get_hrc() == NULL)
{
error_found = true;

if (message_string_ptr != NULL)
{
*message_string_ptr += "setup_splash_screen()\n";
*message_string_ptr += " OpenGL failed to open.\n";
*message_string_ptr += local_message_string.c_str();
local_message_string.erase();
}
}
else
{
process_record_ptr->set_graphics_parameter_ptr(graphics_parameter_ptr) ;
GetClientRect(hdlg, &client_rect);
width = client_rect.right - client_rect.left;
height = client_rect.bottom - client_rect.top;

graphics_parameter_ptr->set_client_rect(&client_rect);

glViewport(0, 0, width, height);

initialize_cameras(graphics_parameter_ptr, width, height, VK_TAB, VK_TAB, 0.001);

// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
glewExperimental = GL_TRUE;
// Initialize GLEW to setup the OpenGL Function pointers
glewInit();

// Define the viewport dimensions
glViewport(0, 0, width, height);

// Setup some OpenGL options
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
// glEnable(GL_CULL_FACE);

graphics_parameter_ptr->set_non_court_shader_ptr(new SHADER("models\\shaders\\test_non_court_color.shv",
"models\\shaders\\test_non_court_color.shf",
&local_message_string));

if (!local_message_string.empty())
{
error_found = true;
*message_string_ptr += "setup_splash_screen()\n";
*message_string_ptr += " Shaders for non-court items failed to load.\n";
*message_string_ptr += local_message_string.c_str();
local_message_string.erase();
}

graphics_parameter_ptr->set_court_shader_ptr(new SHADER("models\\shaders\\test_court_color.shv",
"models\\shaders\\test_court_color.shf",
&local_message_string));

if (!local_message_string.empty())
{
error_found = true;
*message_string_ptr += "setup_splash_screen()\n";
*message_string_ptr += " Shaders for court failed to load.\n";
*message_string_ptr += local_message_string.c_str();
local_message_string.erase();
}
}
}

if (!error_found)
{
graphics_parameter_ptr->set_court_model_ptr(new MODEL("models\\court\\01_1.obj"));
graphics_parameter_ptr->set_net_text_model_ptr(new MODEL("models\\text\\net.obj"));
graphics_parameter_ptr->set_results_text_model_ptr(new MODEL("models\\text\\results.obj"));

// Ambient light's color is determined by the colors of the light, playing surface,
// "Net" text and "Results" text.
glm::vec3 color;

graphics_parameter_ptr->setup_diffuse_colors();

graphics_parameter_ptr->set_setup_is_valid(!error_found);
}
else
{
process_record_ptr->set_graphics_parameter_ptr(NULL);
delete graphics_parameter_ptr;
}

return !error_found;
}

This code is called form the message loop:


void process_simple_graphic_transform_timer(HWND hdlg,
GRAPHICS_PARAMETER *graphics_parameter_ptr)
{
if (graphics_parameter_ptr->get_active())
{
glm::mat4 court_model;

SHADER *non_court_shader_ptr = graphics_parameter_ptr->get_non_court_shader_ptr();

// The court image will be influenced by lighting, so it get's it's own shader.
SHADER *court_shader_ptr = graphics_parameter_ptr->get_court_shader_ptr();

windows_camera_wrapper_basic::CAMERA_WRAPPER *selected_camera_wrapper_ptr =
graphics_parameter_ptr->get_free_camera_wrapper_ptr();

if (selected_camera_wrapper_ptr != NULL)
{
opengl_camera::CAMERA *camera_ptr = selected_camera_wrapper_ptr->get_camera();

float width = float(graphics_parameter_ptr->get_client_rect()->right -
graphics_parameter_ptr->get_client_rect()->left);

float height = float(graphics_parameter_ptr->get_client_rect()->bottom -
graphics_parameter_ptr->get_client_rect()->top);

GLfloat court_tilt = glm::radians(COURT_TILT_X_DEGREES);
GLfloat adjusted_rotation_index = 0.0;

// Clear the colorbuffer
// glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
if (graphics_parameter_ptr->get_setup_is_valid())
{
selected_camera_wrapper_ptr->calculate_delta_time();

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Draw the court.
court_shader_ptr->use();

glUniform4f(glGetUniformLocation(court_shader_ptr->Program, "total_ambient_color"),
graphics_parameter_ptr->get_total_ambient_color()->r,
graphics_parameter_ptr->get_total_ambient_color()->g,
graphics_parameter_ptr->get_total_ambient_color()->b, 1.0f);

// Transformation matrices

// glEnable(GL_CULL_FACE);

adjusted_rotation_index = -selected_camera_wrapper_ptr->get_current_frame() / TIMER_THROTTLE_VALUE;

court_model = glm::rotate(court_model, court_tilt, glm::vec3(1.0, 0.0, 0.0));
court_model = glm::rotate(court_model, adjusted_rotation_index, glm::vec3(0.0, 1.0, 0.0));

glUniformMatrix4fv(glGetUniformLocation(court_shad er_ptr->Program, "model"),
1,
GL_FALSE,
glm::value_ptr(court_model));

position_free_camera(camera_ptr, court_shader_ptr, width, height);

graphics_parameter_ptr->get_court_model_ptr()->Draw(*court_shader_ptr);
// glDisable(GL_CULL_FACE);

// Draw the "Net" text.
non_court_shader_ptr->use();

glm::mat4 model = glm::mat4();

glUniformMatrix4fv(glGetUniformLocation(non_court_ shader_ptr->Program, "model"),
1,
GL_FALSE,
glm::value_ptr(model));

graphics_parameter_ptr->get_net_text_model_ptr()->Draw(*non_court_shader_ptr);

// Draw the "Results" text.
position_free_camera(camera_ptr, court_shader_ptr, width, height);

model = glm::mat4();

glUniformMatrix4fv(glGetUniformLocation(non_court_ shader_ptr->Program, "model"),
1,
GL_FALSE,
glm::value_ptr(model));

graphics_parameter_ptr->get_results_text_model_ptr()->Draw(*non_court_shader_ptr);
}

// Swap the buffers
SwapBuffers(graphics_parameter_ptr->get_hdc());
}
}
}

What am I doing that's different than this fragment of code taken from the tutorial's sample?


GLint vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

Here's the fragment shader the sample is talking to:


#version 330 core
out vec4 color;

uniform vec4 ourColor; // We set this variable in the OpenGL code.

void main()
{
color = ourColor;
}

Here's what looks to me to be a functionally identical fragment from my code:


glUniform4f(glGetUniformLocation(court_shader_ptr->Program, "total_ambient_color"),
graphics_parameter_ptr->get_total_ambient_color()->r,
graphics_parameter_ptr->get_total_ambient_color()->g,
graphics_parameter_ptr->get_total_ambient_color()->b, 1.0f);


The court draws fine when I pass the uniform to it's fragment shader.

The problem is, that act blows the text, drawn by a different shader, out of the water.

Sorry for the length of this post. I'm stumped.

Thanks
Larry

Silence
04-17-2017, 11:29 PM
Different things you should try since this is unclear from your post:

Call glGetError as often as necessary (after each gl call for example).
Get shaders compilation and link logs.

larryl
04-18-2017, 06:11 AM
Thanks Silence:

It works now!

glGetError() showed one problem when I tried to run glGetUniformLocation() for a fragment I hadn't selected with glUseProgram(). But I was using this (https://learnopengl.com/#!Getting-started/Shaders) as an example:

GLint vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

So I'm not sure.

What's with the "f" at the end of of the floating point constants? I think I've seen OpenGL code work when I used it and fail when I didn't. I've written hundreds of thousands of lines of C++ without using it before, but I'm seeing it in OpenGL sample code.

After that was resolved, glGetError() pointed me to another problem. It turned out I was setting the projection and view matrix in the court's shader when I needed the text's shader to adjust the camera's view. That's the vertex shader's job. I have no idea why that worked if I used a hard coded ambient color in the court's fragment shader.

I appreciate this guidance. It's much more valuable to be shown a tool, like glGetError(), than be shown the answer.

Thanks Again
Larry

Silence
04-18-2017, 06:27 AM
What's with the "f" at the end of of the floating point constants?

If you mean the f at 0.0f and 1.0f, that just means to the compiler that the constant is a float, not a double, which the latter is the default.

Glad that the hints helped you to fix your problems.

mhagain
04-18-2017, 06:37 AM
It's difficult to say what the cause of your problem is because you have most everything hidden behind an OO layer (which you don't provide code for); for example:
court_shader_ptr->use();Your cause could be in here, but we don't know what this call actually does, so we can't say for certain if this is the case or not.

If the glProgramUniform (https://www.khronos.org/opengl/wiki/GLAPI/glProgramUniform) calls are available (core since GL 4.1, also available in GL_ARB_separate_shader_objects or in GL_EXT_direct_state_access with -EXT suffix) I'd recommend using them instead of glUseProgram with glUniform; they remove a lot of the uncertaintly and error-prone-ness from setting uniforms by not being reliant on the currently active program.

For troubleshooting I recommend capturing and studying a GLIntercept (https://www.opengl.org/sdk/tools/GLIntercept/) log as a first step - it's a useful tool that can help you check that the GL calls you expect to be made actually are being made.

The "f" suffix on a floating-point constant means that at compile-time it's treated as a float; without the "f" it would be a double. So:

1.0; // this is a double
1.0f; // this is a float

larryl
04-18-2017, 01:39 PM
Thanks mhagain:

One of the tools offered in this (https://learnopengl.com/#!Getting-started/Shaders) tutorial's sample code conveniently abstracts the tedious details needed to work with shaders. The class constructor loads the shaders and runs the compile and link tests. Use() is a call to glUseProgram().

The MODEL (https://learnopengl.com/#!Model-Loading/Mesh) class abstracts the interface to Assimp.

I knew the post was going to be painfully long. I was trying to keep it as small as I could. Providing the C++ as pseudo code.

GLIntercept looks interesting.

The GLSL degugger on this page also intrigues me. Has anybody used it?

Larry