I tried asking this on reddit but I didn't get any answers so I might as well try here
So I'm making a program to generate some terrain and it's not giving me the results I expected. It's drawing black pillars. Lots of code coming up btw.
This is the article that I'm using https://learnopengl.com/Guest-Articles/2021/Tessellation/Tessellation
Even though it's written in C++ I rewrote it in C, the language I'm using.
This is the vertex shader
#version 410 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 tex;
out vec2 tex_coord;
void main()
{
// set position and sets texture (uv) coordinates
gl_Position = vec4(pos, 1.0);
tex_coord = tex;
}
This is the fragment shader
#version 410 core
in float height;
out vec4 color;
void main()
{
float h = (height+16)/64.0f;
color = vec4(h,h,h,1.0);
}
This is the tessellation control shader
#version 410 core
layout(vertices=4) out;
uniform mat4 model;
uniform mat4 view;
in vec2 tex_coord[];
out vec2 texture_coord[];
void main()
{
// essentially sets render distance
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
texture_coord[gl_InvocationID] = tex_coord[gl_InvocationID];
if(gl_InvocationID == 0)
{
const int MIN_TESS_LEVEL = 4;
const int MAX_TESS_LEVEL = 64;
const float MIN_DISTANCE = 20;
const float MAX_DISTANCE = 800;
vec4 eye_space_pos_00 = view * model * gl_in[0].gl_Position;
vec4 eye_space_pos_01 = view * model * gl_in[1].gl_Position;
vec4 eye_space_pos_10 = view * model * gl_in[2].gl_Position;
vec4 eye_space_pos_11 = view * model * gl_in[3].gl_Position;
// "distance" from camera scaled between 0 and 1
float distance_00 = clamp( (abs(eye_space_pos_00.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 );
float distance_01 = clamp( (abs(eye_space_pos_01.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 );
float distance_10 = clamp( (abs(eye_space_pos_10.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 );
float distance_11 = clamp( (abs(eye_space_pos_11.z) - MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0 );
float tess_level_0 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance_10, distance_00) );
float tess_level_1 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance_00, distance_01) );
float tess_level_2 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance_01, distance_11) );
float tess_level_3 = mix( MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance_11, distance_10) );
gl_TessLevelOuter[0] = tess_level_0;
gl_TessLevelOuter[1] = tess_level_1;
gl_TessLevelOuter[2] = tess_level_2;
gl_TessLevelOuter[3] = tess_level_3;
gl_TessLevelInner[0] = max(tess_level_1, tess_level_3);
gl_TessLevelInner[1] = max(tess_level_0, tess_level_2);
}
}
This is the tessellation evaluation shader
#version 410 core
layout(quads, fractional_odd_spacing, ccw) in;
uniform sampler2D height_map;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
in vec2 texture_coord[];
out float height;
void main()
{
// get the texture coordinate of point (x, y, z) in the terrain
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;
vec2 t_00 = texture_coord[0];
vec2 t_01 = texture_coord[1];
vec2 t_10 = texture_coord[2];
vec2 t_11 = texture_coord[3];
vec2 t_0 = (t_01 - t_00) * u + t_00;
vec2 t_1 = (t_11 - t_10) * u + t_10;
vec2 tex_coord = (t_1 - t_0) * v + t_0;
height = texture(height_map, tex_coord).g * 64.0 - 16.0;
vec4 p_00 = gl_in[0].gl_Position;
vec4 p_01 = gl_in[1].gl_Position;
vec4 p_10 = gl_in[2].gl_Position;
vec4 p_11 = gl_in[3].gl_Position;
vec4 u_vec = p_01 - p_00;
vec4 v_vec = p_10 - p_00;
vec4 normal = normalize( vec4(cross(v_vec.xyz, u_vec.xyz), 0) );
vec4 p_0 = (p_01 - p_00) * u + p_00;
vec4 p_1 = (p_11 - p_10) * u + p_10;
vec4 p = (p_1 - p_0) * v + p_0 + normal * height;
gl_Position = projection * view * model * p;
}
This is the initialization for GLFW
// init glfw
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif // __APPLE__
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// check to see if glfw was initialized
if(!glfwInit())
{
printf("GLFW is not ok.\n");
}
This is where I loaded the height map
// height map
unsigned int texture;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load the image
int width, height, nr_components;
short unsigned int* data = stbi_load_16("textures/river_heightmap.png", &width, &height, &nr_components, 0);
if(data)
{
// bind texture and specify its data and how to manage it
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16, width, height, 0, GL_RED, GL_UNSIGNED_SHORT, data);
// set the height map
set_int(shader_program, "height_map", 0);
printf("Loaded a height map of %i x %i\n", width, height);
}
else
{
printf("Failed to load height map\n");
}
stbi_image_free(data);
This is where I uploaded the terrain data to the GPU
float vertices[1000];
unsigned rez = 20;
for(unsigned i = 0; i <= rez-1; i++)
{
for(unsigned j = 0; j <= rez-1; j++)
{
push_back_float(-width/2.0f + width*i/(float)rez, vertices); // v.x
push_back_float(0.0f, vertices); // v.y
push_back_float(-height/2.0f + height*j/(float)rez, vertices); // v.z
push_back_float(i / (float)rez, vertices); // u
push_back_float(j / (float)rez, vertices); // v
push_back_float(-width/2.0f + width*(i+1)/(float)rez, vertices); // v.x
push_back_float(0.0f, vertices); // v.y
push_back_float(-height/2.0f + height*j/(float)rez, vertices); // v.z
push_back_float((i+1) / (float)rez, vertices); // u
push_back_float(j / (float)rez, vertices); // v
push_back_float(-width/2.0f + width*i/(float)rez, vertices); // v.x
push_back_float(0.0f, vertices); // v.y
push_back_float(-height/2.0f + height*(j+1)/(float)rez, vertices); // v.z
push_back_float(i / (float)rez, vertices); // u
push_back_float((j+1) / (float)rez, vertices); // v
push_back_float(-width/2.0f + width*(i+1)/(float)rez, vertices); // v.x
push_back_float(0.0f, vertices); // v.y
push_back_float(-height/2.0f + height*(j+1)/(float)rez, vertices); // v.z
push_back_float((i+1) / (float)rez, vertices); // u
push_back_float((j+1) / (float)rez, vertices); // v
}
}
printf("Loaded %i patches of 4 control points each\n", rez*rez);
printf("Processing %i vertices in vertex shader\n", rez*rez*4);
This is where I set up the VAO and VBO
unsigned int vao, vbo;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, (float)(sizeof(vertices)/sizeof(vertices[0])) * sizeof(float), &vertices[0], GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
glPatchParameteri(GL_PATCH_VERTICES, NUM_PATCH_PTS);
This is the main loop and the buffer deletion
// game loop
while (!glfwWindowShouldClose(window))
{
// per-frame time logic and FPS counter
float current_frame = glfwGetTime();
delta_time = current_frame - last_frame;
last_frame = current_frame;
// fps counter, uncomment if needed
//printf("%i ms %f FPS\n", (int)delta_time, 1.0f / delta_time);
// input
keyboard_input(window);
// render
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// shader
use_shader(shader_program);
// view/projection transformations
mat4 projection = GLM_MAT4_IDENTITY_INIT;
glm_perspective(glm_rad(ccamera->zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f, projection);
mat4 view = GLM_MAT4_IDENTITY_INIT;
camera_get_view_matrix(view);
set_mat_4(shader_program, "projection", projection);
set_mat_4(shader_program, "view", view);
// world transformation
mat4 model = GLM_MAT4_IDENTITY_INIT;
set_mat_4(shader_program, "model", model);
// render terrain
glBindVertexArray(vao);
glDrawArrays(GL_PATCHES, 0, NUM_PATCH_PTS*rez*rez);
// glfw: swap buffers and poll IO events
glfwSwapBuffers(window);
glfwPollEvents();
}
// deallocate resources that outlived their purpose
shader_clean_up(shader_program);
// clean up glfw
glfwTerminate();
return 0;
These are the screenshots of the RenderDoc report
If you need any more information tell me so I can add more screenshots and code if needed.
Edit: The shader loader, the original code is from here if you wanna have a look at that: https://github.com/CraftingInC/LearnOpenGLInC/blob/master/includes/learnopengl/shader.h
Note: I did change a few things around from the original so here is my version
#ifndef SHADER_H_INCLUDED
#define SHADER_H_INCLUDED
#include <GL/glew.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <CGLM/cglm.h>
// loads the shader then copies the data into a char variable and returns the shader id
int load_shader_file(const char* shader_file_name, int shader_type)
{
FILE* shader_file = fopen(shader_file_name, "rb");
if(shader_file != NULL)
{
char* shader_char;
fseek(shader_file, 0, SEEK_END);
size_t total_size = ftell(shader_file);
rewind(shader_file);
shader_char = (char*)malloc(total_size + 1);
fread(shader_char, 1, total_size, shader_file);
shader_char[total_size] = 0;
fclose(shader_file);
GLuint shader_id = 0;
shader_id = glCreateShader(shader_type);
glShaderSource(shader_id, 1, (const char**)&shader_char, NULL);
glCompileShader(shader_id);
GLint shader_compiled;
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &shader_compiled);
if(shader_compiled != GL_TRUE)
{
GLsizei log_length;
GLchar log[1024];
glGetShaderInfoLog(shader_id, sizeof(log), &log_length, log);
printf("Error in %s: %s\n", shader_file_name, log);
}
else
{
return shader_id;
}
}
else
{
printf("Unable to open file %s\n", shader_file_name);
}
return -1;
}
// checks for shader program errors
void check_for_errors(int shader_program)
{
int worked;
char info_log[1024];
glGetProgramiv(shader_program, GL_LINK_STATUS, &worked);
if(!worked)
{
glGetShaderInfoLog(shader_program, 1024, NULL, info_log);
printf("Program linking failed: %s\n", info_log);
}
}
// takes the already loaded shaders and attaches them to the shader program then deletes them (debugs if necessary) and finally returns the shader program
unsigned int load_glsl_shaders(const char* frag_shader_path, const char* vert_shader_path,
const char* tess_control_path, const char* tess_eval_path)
{
unsigned int fragment, vertex, tess_control, tess_eval, program;
fragment = load_shader_file(frag_shader_path, GL_FRAGMENT_SHADER);
vertex = load_shader_file(vert_shader_path, GL_VERTEX_SHADER);
tess_control = load_shader_file(tess_control_path, GL_TESS_CONTROL_SHADER);
tess_eval = load_shader_file(tess_eval_path, GL_TESS_EVALUATION_SHADER);
program = glCreateProgram();
glAttachShader(program, fragment);
glAttachShader(program, vertex);
glAttachShader(program, tess_control);
glAttachShader(program, tess_eval);
glLinkProgram(program);
check_for_errors(program);
glDeleteShader(fragment);
glDeleteShader(vertex);
glDeleteShader(tess_control);
glDeleteShader(tess_eval);
return program;
}
// compiles the shaders and shader program (also debugs if necessary) then returns the shader program
unsigned int load_embedded_shaders(const char* vertex_shader_text, const char* fragment_shader_text,
const char* tess_control_text, const char* tess_eval_text)
{
GLchar vertex_shader, fragment_shader, tess_control_shader, tess_eval_shader, shader_program;
int compiled;
char info_log[512];
// compile fragment shader
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
printf("Fragment shader failed: %s\n", info_log);
}
// compile vertex shader
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
printf("Vertex shader failed: %s\n", info_log);
}
// compile tessellation control shader
tess_control_shader = glCreateShader(GL_TESS_CONTROL_SHADER);
glShaderSource(tess_control_shader, 1, &tess_control_text, NULL);
glCompileShader(tess_control_shader);
glGetShaderiv(tess_control_shader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
glGetShaderInfoLog(tess_control_shader, 512, NULL, info_log);
printf("Tessellation control shader failed: %s\n", info_log);
}
// compile tessellation evaluation shader
tess_eval_shader = glCreateShader(GL_TESS_EVALUATION_SHADER);
glShaderSource(tess_eval_shader, 1, &tess_eval_text, NULL);
glCompileShader(tess_eval_shader);
glGetShaderiv(tess_eval_shader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
glGetShaderInfoLog(tess_eval_shader, 512, NULL, info_log);
printf("Tessellation evaluation shader failed: %s\n", info_log);
}
// compile the shader program
shader_program = glCreateProgram();
glAttachShader(shader_program, fragment_shader);
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, tess_control_shader);
glAttachShader(shader_program, tess_eval_shader);
glLinkProgram(shader_program);
check_for_errors(shader_program);
// deletes the shaders
glDeleteShader(fragment_shader);
glDeleteShader(vertex_shader);
glDeleteShader(tess_control_shader);
glDeleteShader(tess_eval_shader);
// return shader program
return shader_program;
}
// deletes the shader program
void shader_clean_up(unsigned int shader_program)
{
glDeleteProgram(shader_program);
}
// uses the shader program
void use_shader(unsigned int shader_program)
{
glUseProgram(shader_program);
}
// sets boolean values in the shader program / shader(s) that are linked to it
void set_bool(unsigned int shader_program, const char* name, bool value)
{
glUniform1i(glGetUniformLocation(shader_program, name), (int)value);
}
// sets integer values in the shader program / shader(s) that are linked to it
void set_int(unsigned int shader_program, const char* name, int value)
{
glUniform1i(glGetUniformLocation(shader_program, name), value);
}
// sets floating point values in the shader program / shader(s) that are linked to it
void set_float(unsigned int shader_program, const char* name, float value)
{
glUniform1f(glGetUniformLocation(shader_program, name), value);
}
// sets 2 dimensional matrices in the shader program / shader(s) that are linked to it
void set_mat_2(unsigned int shader_program, const char* name, const mat2 mat)
{
glUniformMatrix2fv(glGetUniformLocation(shader_program, name), 1, GL_FALSE, &mat[0][0]);
}
// sets 3 dimensional matrices in the shader program / shader(s) that are linked to it
void set_mat_3(unsigned int shader_program, const char* name, const mat3 mat)
{
glUniformMatrix3fv(glGetUniformLocation(shader_program, name), 1, GL_FALSE, &mat[0][0]);
}
// sets 4 dimensional matrices in the shader program / shader(s) that are linked to it
void set_mat_4(unsigned int shader_program, const char* name, const mat4 mat)
{
glUniformMatrix4fv(glGetUniformLocation(shader_program, name), 1, GL_FALSE, &mat[0][0]);
}
// sets 2 dimensional vectors in the shader program / shader(s) that are linked to it
void set_vec_2(unsigned int shader_program, const char* name, const vec2 value)
{
glUniform2fv(glGetUniformLocation(shader_program, name), 1, &value[0]);
}
// sets 2 dimensional vectors with 3d space directions in the shader program / shader(s) that are linked to it
void set_vec_2_xyz(unsigned int shader_program, const char* name, float x, float y)
{
glUniform2f(glGetUniformLocation(shader_program, name), x, y);
}
// sets 3 dimensional vectors in the shader program / shader(s) that are linked to it
void set_vec_3(unsigned int shader_program, const char* name, const vec3 value)
{
glUniform3fv(glGetUniformLocation(shader_program, name), 1, &value[0]);
}
// sets 3 dimensional vectors with 3d space directions in the shader program / shader(s) that are linked to it
void set_vec_3_xyz(unsigned int shader_program, const char* name, float x, float y, float z)
{
glUniform3f(glGetUniformLocation(shader_program, name), x, y, z);
}
// sets 4 dimensional vectors in the shader program / shader(s) that are linked to it
void set_vec_4(unsigned int shader_program, const char* name, const vec4 value)
{
glUniform4fv(glGetUniformLocation(shader_program, name), 1, &value[0]);
}
// sets 4 dimensional vectors with 3d space directions that include the w (the component that scales the other dimensions) in the shader program / shader(s) that are linked to it
void set_vec_4_wyzx(unsigned int shader_program, const char* name, float x, float y, float z, float w)
{
glUniform4f(glGetUniformLocation(shader_program, name), x, y, z, w);
}
#endif // SHADER_H_INCLUDED
Edit 2: Shaders getting loaded
unsigned int shader_program = load_glsl_shaders("frag.vert", "vert.vert", "gpu_tcs.vert", "gpu_tes.vert");
shader_program
\$\endgroup\$shader_program
that is later used inuse_shader(shader_program);
It seems that glUseProgram returns an error GL_INVALID_OPERATION in one of the RenderDoc captures. \$\endgroup\$use_shader(shader_program);
has the codeglUseProgram(shader_program)
\$\endgroup\$shader_program = load_embedded_shaders...
and the previous shader definitions. \$\endgroup\$