쉐이더 입력 채널

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

type

기본적으로 GLSL에서는 cpp과 같이
int, float, double, bool, uint이런 타입을 사용할 수 있고
추가적으로 vecor, matrix타입이 있음

n : 2,3,4의 숫자

  • vecn: 부동소수점 벡터
  • bvecn: 불리언 벡터
  • ivecn: 정수형 벡터
  • uvecn: 부호없는 정수형 벡터
  • dvecn: double형 벡터

벡터 swizzling

걍 편의성임

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의 outvertexColor vec4변수가 있음
그리고 fragment shader에서 in으로 vec4변수인 vertexColor를 입력받음

즉, 두 변수간의 이름이 같으면 gl이 자동적으로 연결해준다는거임ㄷㄷ

uniform

glsl에서는 uniform이라는 변수를 만들수 있음

이건 cpu, gpu모두 공용으로 사용할 수 있는 변수를 만드는거임
그리고 각 쉐이더 또한 이 변수를 같이 사용할 수 있음

사용법은 간단함

uniform vec4 smth;

이런식으로 쓰면 되는거임

주의 : uniform선언해두고 안쓰면 자동적을 컴파일 타임에 걸러냄...

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쪽을 수정해줌

  1. glfwGetTime으로 현재시간을 가져온 후, 0.0~1.0사이의 값으로 변환
  2. glGetUniformLocation으로 특정 프로그램에서 특정 uniform 변수를 문자열로 찾음
    만약 return 값이 -1이라면, 문자열에 해당되는 uniform을 해당 shader program에서 찾지 못한거임
  3. 랜덤함수 만들음
  4. use program
    • uniform 변수를 찾는데 glUseProgram을 할 필요는 없지만, 사용하기 위해선 glUseProgram을 실행해햐함
  5. 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);
}

더 많은 속성(glVertexAttribPointer offset설정)

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에서 정점 매핑을 수정해줘야함

  • vertex position : 첫 인덱스부터 3개 이후, 6개씩 offset
  • vertex color : 3번 인덱스부터 3개 이후, 6개씩 offset
// 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();
}

이럼 끝임!

잘 출력댐!

profile
그래픽스 하는 퍼그

0개의 댓글