continue with specification
This commit is contained in:
247
shader_experiments/basic_triangle/main.cpp
Normal file
247
shader_experiments/basic_triangle/main.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* MIT License
|
||||
* Author: Mark Allyn
|
||||
*
|
||||
* basic_triangle/main.cpp
|
||||
*
|
||||
* Learning exercise: draw a single triangle using a vertex shader and
|
||||
* fragment shader. The vertex shader places three corners in clip space;
|
||||
* the fragment shader colours each pixel by interpolating the per-vertex
|
||||
* colours the GPU receives.
|
||||
*
|
||||
* Key concepts demonstrated
|
||||
* 1. Compiling and linking a GLSL shader program at runtime
|
||||
* 2. Uploading vertex data to the GPU via a VAO + VBO
|
||||
* 3. How the rasteriser interpolates per-vertex attributes (colour here)
|
||||
* across a primitive before the fragment shader runs
|
||||
* 4. The minimal render loop: clear → draw → swap
|
||||
*/
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GLSL source — kept inline so the whole exercise lives in one file
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Vertex shader
|
||||
*
|
||||
* Runs once per vertex (three times for our triangle).
|
||||
* layout(location = 0) → slot 0 in the VAO: the 2-D position
|
||||
* layout(location = 1) → slot 1 in the VAO: the RGB colour
|
||||
*
|
||||
* gl_Position must be set; it tells the rasteriser where on screen the
|
||||
* vertex lands. We set w = 1.0 so the position is not divided (no
|
||||
* perspective).
|
||||
*
|
||||
* vertex_color is an "out" variable — the rasteriser will smoothly
|
||||
* interpolate it across the triangle and hand the result to the fragment
|
||||
* shader as an "in" variable with the same name.
|
||||
*/
|
||||
static const char* vertex_shader_src = R"glsl(
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec2 position;
|
||||
layout(location = 1) in vec3 color;
|
||||
|
||||
out vec3 vertex_color; // passed to the fragment shader
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
vertex_color = color;
|
||||
}
|
||||
)glsl";
|
||||
|
||||
/*
|
||||
* Fragment shader
|
||||
*
|
||||
* Runs once per screen pixel that the rasteriser decides is inside the
|
||||
* triangle. It receives the interpolated vertex_color and outputs a
|
||||
* final RGBA colour for that pixel.
|
||||
*/
|
||||
static const char* fragment_shader_src = R"glsl(
|
||||
#version 330 core
|
||||
|
||||
in vec3 vertex_color; // interpolated from the three vertices
|
||||
out vec4 frag_color; // final pixel colour (RGBA)
|
||||
|
||||
void main()
|
||||
{
|
||||
frag_color = vec4(vertex_color, 1.0); // alpha = 1 (fully opaque)
|
||||
}
|
||||
)glsl";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: compile one shader stage and report any errors
|
||||
// ---------------------------------------------------------------------------
|
||||
static GLuint compile_shader(GLenum type, const char* src)
|
||||
{
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &src, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint ok = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
char log[512];
|
||||
glGetShaderInfoLog(shader, sizeof(log), nullptr, log);
|
||||
std::fprintf(stderr, "Shader compile error:\n%s\n", log);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: link vertex + fragment shaders into a program
|
||||
// ---------------------------------------------------------------------------
|
||||
static GLuint build_program(const char* vert_src, const char* frag_src)
|
||||
{
|
||||
GLuint vert = compile_shader(GL_VERTEX_SHADER, vert_src);
|
||||
GLuint frag = compile_shader(GL_FRAGMENT_SHADER, frag_src);
|
||||
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vert);
|
||||
glAttachShader(program, frag);
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint ok = 0;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
char log[512];
|
||||
glGetProgramInfoLog(program, sizeof(log), nullptr, log);
|
||||
std::fprintf(stderr, "Program link error:\n%s\n", log);
|
||||
}
|
||||
|
||||
// Once linked the individual stage objects are no longer needed
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// main
|
||||
// ---------------------------------------------------------------------------
|
||||
int main()
|
||||
{
|
||||
// -----------------------------------------------------------------------
|
||||
// Window setup
|
||||
// -----------------------------------------------------------------------
|
||||
if (!glfwInit())
|
||||
{
|
||||
std::fprintf(stderr, "glfwInit failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
GLFWwindow* window = glfwCreateWindow(800, 600, "Basic Triangle", nullptr, nullptr);
|
||||
if (!window)
|
||||
{
|
||||
std::fprintf(stderr, "glfwCreateWindow failed\n");
|
||||
glfwTerminate();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
|
||||
{
|
||||
std::fprintf(stderr, "GLAD init failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Vertex data
|
||||
//
|
||||
// Clip space runs from -1 to +1 on both axes. Each row below is:
|
||||
// x y R G B
|
||||
// The GPU interpolates R,G,B across the triangle surface.
|
||||
// -----------------------------------------------------------------------
|
||||
float vertices[] = {
|
||||
// position color (RGB)
|
||||
0.0f, 0.5f, 1.0f, 0.0f, 0.0f, // top — red
|
||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // left — green
|
||||
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // right — blue
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// GPU objects
|
||||
//
|
||||
// VAO — remembers which VBO(s) and attribute layouts belong together
|
||||
// VBO — raw buffer holding the vertex data above
|
||||
// -----------------------------------------------------------------------
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
/* Tell OpenGL how to read attribute 0 (position):
|
||||
- 2 floats
|
||||
- stride = 5 floats (2 position + 3 colour)
|
||||
- offset = 0 */
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,
|
||||
5 * static_cast<int>(sizeof(float)),
|
||||
reinterpret_cast<void*>(0));
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
/* Attribute 1 (colour):
|
||||
- 3 floats
|
||||
- same stride
|
||||
- offset = 2 floats in */
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
||||
5 * static_cast<int>(sizeof(float)),
|
||||
reinterpret_cast<void*>(2 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glBindVertexArray(0); // unbind so later state changes don't affect it
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Shader program
|
||||
// -----------------------------------------------------------------------
|
||||
GLuint shader_program = build_program(vertex_shader_src, fragment_shader_src);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Render loop
|
||||
// -----------------------------------------------------------------------
|
||||
while (!glfwWindowShouldClose(window))
|
||||
{
|
||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
||||
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glUseProgram(shader_program);
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3); // 3 vertices = 1 triangle
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// -----------------------------------------------------------------------
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
glDeleteBuffers(1, &vbo);
|
||||
glDeleteProgram(shader_program);
|
||||
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user