
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0";
지난번에 이런 glsl을 만들었음
형식은 아래와 같음
#version version_number
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
void main()
{
// process input(s) and do some weird graphics stuff
...
// output processed stuff to output variable
out_variable_name = weird_stuff_we_processed;
}
여기서 in은 입력 변수고 out은 출력 변수임
입력변수를 버텍스 속성이라고도 함
그래픽카드에서는 최대로 in을 받을 수 있는 변수 갯수가 정해져 잇음
int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
이런 코드를 실행해보면,

이런 로그가 출력됨
즉, 내 그래픽카드는 최대 16개의 버텍스 속성을 동시에 처리할 수 있음
그리고 각 버텍스 속성마다 최대 성분은 4성분으로 vec4, mat4와 같은 클래스를 의미함
주의 : vec4가 아니라 vec2만 썼다고 총 32개의 버텍스 속성을 쓸 수 있는게 아님!!
최대 개수가 16개인거임!
기본적으로 GLSL에서는 cpp과 같이
int, float, double, bool, uint이런 타입을 사용할 수 있고
추가적으로 vecor, matrix타입이 있음
n : 2,3,4의 숫자
걍 편의성임
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
이런식으로 각 vector의 요소에 맞춰서 다른 벡터를 만들수도 있고 막 그럼
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
이렇게 ㅇㅇ
vertex shader#version 330 core
layout (location = 0) in vec3 aPos;
out vec4 vertexColor;
void main()
{
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
}
fragment shader#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // in과 out의 같은 이름
void main()
{
FragColor = vertexColor;
}
지금 보면 vertex shader의 out인 vertexColor vec4변수가 있음
그리고 fragment shader에서 in으로 vec4변수인 vertexColor를 입력받음
즉, 두 변수간의 이름이 같으면 gl이 자동적으로 연결해준다는거임ㄷㄷ
glsl에서는 uniform이라는 변수를 만들수 있음
이건 cpu, gpu모두 공용으로 사용할 수 있는 변수를 만드는거임
그리고 각 쉐이더 또한 이 변수를 같이 사용할 수 있음
사용법은 간단함
uniform vec4 smth;
이런식으로 쓰면 되는거임
주의 : uniform선언해두고 안쓰면 자동적을 컴파일 타임에 걸러냄...
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;"
"void main()\n"
"{\n"
" FragColor = ourColor;\n"
"}\0";
이런 fragment shader에 uniform을 추가함
// uniform example
float time = glfwGetTime();
float color = sin(time) / 2.0f + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<float> dist(0.0, 1.0);
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, color * dist(gen), color * dist(gen), color * dist(gen), 1.0f); //0.0~1.0사이의 랜덤 값
// 10. delete linked shader
glBindVertexArray(VAO);
그리고 while문에서 UseProgram쪽을 수정해줌
glfwGetTime으로 현재시간을 가져온 후, 0.0~1.0사이의 값으로 변환glGetUniformLocation으로 특정 프로그램에서 특정 uniform 변수를 문자열로 찾음glUseProgram을 할 필요는 없지만, 사용하기 위해선 glUseProgram을 실행해햐함glUniform4f함수를 이용해서 vec4(부동소수점 vec4)타입인 uniform변수를 업데이트함
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <random>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void process_input(GLFWwindow *window);
float vertices[] =
{
0.5f, 0.5f, 0.0f, // 우상단
0.5f, -0.5f, 0.0f, // 우하단
-0.5f, -0.5f, 0.0f, // 좌하단
-0.5f, 0.5f, 0.0f, // 좌상단
0.6f, 0.6f, 0.0f, //삼각형추가
0.5f, 0.6f, 0.0f,
0.55f, 0.7f, 0.0f,
-0.8f, -0.8f, 0.0f, //wireframe용 삼각형
-0.6f, -0.8f, 0.0f,
-0.7f, -0.6f, 0.0f
};
uint32_t indices[] =
{
0, 1, 3, //첫번째 삼각형 좌표 인덱스
1, 2, 3, //두번째 삼각형 좌표 인덱스
4,5,6
};
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;"
"void main()\n"
"{\n"
" FragColor = ourColor;\n"
"}\0";
int main()
{
constexpr int width = 1280;
constexpr int height = 720;
//glfw init
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//make window with glfw
GLFWwindow* window = glfwCreateWindow(width, height, "HukPak", nullptr, nullptr);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
//GLAD checks gl pointer of GL-functions
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//viewport configuration with custom callback method
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// 1. Vertex shader
uint32_t vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// 2. check vertex shader successfully compiled
int success;
char log[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, log);
std::cout << "ERROR: " << log << std::endl;
}
// 3. fragment shader
uint32_t fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// 4. check fragment shader successfully compiled
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, log);
std::cout << "ERROR: " << log << std::endl;
}
// 5. shader program
uint32_t shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// 6. check shader program successfully compiled
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgram, 512, nullptr, log);
std::cout << "ERROR: " << log << std::endl;
}
// 7. delete already linked shader
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 8. VAO
uint32_t VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// 9. Vertex, VBO
uint32_t VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 9-1. EBO
uint32_t EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 9-2. vertex attributes linking
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);
glEnableVertexAttribArray(0);
// 9-3. tell gl, binding is over. So don't let bind more to VAO
glBindVertexArray(0);
//run window
while(!glfwWindowShouldClose(window))
{
process_input(window);
glClearColor(.0f, .0f, .0f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
// uniform example
float time = glfwGetTime();
float color = sin(time) / 2.0f + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<float> dist(0.0, 1.0);
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, color * dist(gen), color * dist(gen), color * dist(gen), 1.0f);
// 10. delete linked shader
glBindVertexArray(VAO);
// additional triangle render with DrawArrays + DrawElements
glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_TRIANGLES, 4, 3);
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// draw 1 trangle with wireframe mode
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawArrays(GL_TRIANGLES, 7, 3);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindVertexArray(0);
//swapping buffer frame, and execute event as like input or something
glfwSwapBuffers(window);
glfwPollEvents();
}
//optional, don't need it but keep do it until used to OpenGL
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
glDeleteVertexArrays(1, &VAO);
int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
//stop glfw with runtime over
glfwTerminate();
return 0;
}
//viewport configuration callback method
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
//key input callback
void process_input(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
float vertices[] =
{
//positions //colors
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 우상단
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 우하단
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 좌하단
};
먼저 vertices를 수정좀 해줌
삼각형만 출력할그그든ㅇㅇ
우리의 목적은 vertex마다 다른 색상을 가지도록 하는것임
따라서 우리는 vertex shader에서 설정을 해줘야함
const char *vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n" //vertex position attributes
"layout (location = 1) in vec3 aColor;\n" //vertex color attributes
"out vec3 ourColor;\n" //out in connecting
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"
" ourColor = aColor;\n"
"}\0";
const char *fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n" //out in connecting
"void main()\n"
"{\n"
" FragColor = ourColor;\n"
"}\0";
shader는 이런식으로 수정해줌
이번에는 vertex에 각각 색상을 지정해줌
그리고 그걸 out을 통해 fragment로 넘기고
fragment에서는 in을 통해 받음
그리고 지금 우리의 vertices를 살펴보면
[position(f3)-color(f3)] <-> [position(f3)-color(f3)] <->....
이런식으로 되어있음(f3는 float 3개라는 뜻)
따라서 glVertexAttributePointer에서 정점 매핑을 수정해줘야함
// 9-2. vertex attributes linking
//position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)0);
glEnableVertexAttribArray(0);
//color
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
location에 맞춰서 0번 1번 통로 잘 설정해주고
6개씩 offset & 시작 인덱스 설정!

추가적으로 자동 선형 보간이 되는것까지 확인!
선형보간이 어떻게 작동하냐고??
흑빡 - Barycentric Coordinates 이걸 읽어보셈!
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdint.h>
#include <string>
class Shader
{
public:
uint32_t shaderProgramID;
Shader(const char* vertexPath, const char* fragmentPath)
{
// 1. 쉐이더 코드와 쉐이더 파일 입력스트림 생성
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
//파일 읽을수있는지 확실하게 하기
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
// 2. 쉐이더 파일 읽기
try
{
//각 path에 존재하는 파일 열기
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
//열은 파일을 stringstream으로 읽기
std::stringstream vShaderStream;
std::stringstream fShaderStream;
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
//읽은 후 파일 닫기
vShaderFile.close();
fShaderFile.close();
//읽은 stringstream을 string으로 변환
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHDAER::FILE_NOT_SUCCESSFULLY_READ" << std::endl;
}
//쉐이더 코드를 사용하기 위해 const char* 타입으로 변환
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
//쉐이더 프로그램 만들기
// 1. Vertex shader
uint32_t v = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(v, 1, &vShaderCode, NULL);
glCompileShader(v);
// 2. check vertex shader successfully compiled
int success;
char log[512];
glGetShaderiv(v, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(v, 512, NULL, log);
std::cout << "ERROR: " << log << std::endl;
}
// 3. fragment shader
uint32_t f = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(f, 1, &fShaderCode, NULL);
glCompileShader(f);
// 4. check fragment shader successfully compiled
glGetShaderiv(f, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(f, 512, NULL, log);
std::cout << "ERROR: " << log << std::endl;
}
// 5. shader program
shaderProgramID = glCreateProgram();
glAttachShader(shaderProgramID, v);
glAttachShader(shaderProgramID, f);
glLinkProgram(shaderProgramID);
// 6. check shader program successfully compiled
glGetProgramiv(shaderProgramID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgramID, 512, nullptr, log);
std::cout << "ERROR: " << log << std::endl;
}
// 7. delete already linked shader
glDeleteShader(v);
glDeleteShader(f);
}
~Shader()
{
glDeleteProgram(shaderProgramID);
}
void useShader()
{
glUseProgram(shaderProgramID);
}
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(shaderProgramID, name.c_str()), int(value));
}
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(shaderProgramID, name.c_str()), value);
}
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(shaderProgramID, name.c_str()), value);
}
};
#endif
핵심은 생성자부분임
주석만 잘 읽으면 문제가 없을거니,
주석 꼼꼼히 읽고 코드가 그렇게 어렵지도 않아서,,,
그냥 잘 해석해보길!
이제 본인이 원하는대로 vsh, fsh와 같은 확장자로 만들어서
쉐이더 생성자에 담아주기만 하면 끝임!
Shader shaderProgram(
"somePath/VertexShaderSource.vsh",
"somePath/FragmentShaderSource.fsh");
//...
while()
{
//...
shaderProgram.useShader();
}
이럼 끝임!

잘 출력댐!