LearnOpenGL(12) - glsl에서 커스텀 함수 사용

흑빡·2026년 6월 11일

그래픽스

목록 보기
33/48

다양한 조명 사용

LearnOpenGL(11) - light casters
여기서 우리는 다양한 조명을 어떻게 구현하는지 살펴봤음

그럼 우리는 하나 더 하고싶은거지 이제

이 다양한 조명을 하나의 씬 내부에서 사용하려면 어떻게 해야할까?

커스텀 함수를 사용하면됨

#version 330 core
struct Material
{
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};

struct Light
{
    vec3 position;
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;

    float cutoff; //radian degree
    float outerCutoff; //radian degree
};

out vec4 FragColor;

in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords; //light map용 텍스쳐 좌표

uniform vec3 viewPos;
uniform Material mat;
uniform Light light;


void main()
{
    vec3 lightDir = normalize(light.position - FragPos);

    // light dir과 spot dir의 내적
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutoff - light.outerCutoff;
    float I = clamp((theta - light.outerCutoff) / (epsilon), 0.0f, 1.0f);

    // ambient
    vec3 ambient = light.ambient * texture(mat.diffuse, TexCoords).rgb;

    // diffuse 
    vec3 norm = normalize(Normal);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(mat.diffuse, TexCoords).rgb;

    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), mat.shininess);
    vec3 specular = light.specular * spec * texture(mat.specular, TexCoords).rgb;

    // attenuation
    float distance = length(light.position - FragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));

    ambient *= attenuation;
    diffuse *= attenuation * I;
    specular *= attenuation * I;

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
} 

이게 지금 우리 fragment shader임

우리는 directional light와 point light를 한번에 다루고 싶음

주의사항!!!

구조체 내부에 LearnOpenGL(10) - light maps 여기서 설명한것처럼 opaque타입 변수가 있으면 함수 파라미터로 못넘김!!!

directional light

먼저 directional light구조체를 만들어줌

struct DirLight
{
    vec3 direction;
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

그리고 C, C++처럼 함수가 main보다 아래에 선언되어있으면
컴파일이 안됨!!
그러므로, 위에다가 선언하든지
아래에다 선언하고 위에 전방선언 해주던지 ㅇㅇ

아래가 그 코드!

struct DirLight
{
    vec3 direction;
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

//...

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);

void main()
{
	//... some shading code
}

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    
    // combine results
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    
    return (ambient + diffuse + specular);
} 

point light

마찬가지로 먼저 구조체를 만든다음
함수 전방선언하고
함수 정의함

struct PointLight {    
    vec3 position;
    
    float constant;
    float linear;
    float quadratic;  

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};  
#define NR_POINT_LIGHTS 4  
uniform PointLight pointLights[NR_POINT_LIGHTS];

구조체는 특이사항이 없는데
밑에 배열을 선언했음
저렇게 배열의 크기를 만들어주고 사용하던지
뭐그냥 하드코딩하든지 ㅇㅇ

가변배열도 가능하긴 한데
SSBO, UBO 이런 키워드로 찾아보면 됨

아래는 함수

#version 330 core
struct Material
{
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};

struct DirLight
{
    vec3 direction;
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);

void main()
{
	//... some shading code
}

vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    
    // attenuation
    float distance    = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance +
    light.quadratic * (distance * distance));
    
    // combine results
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    ambient  *= attenuation;
    diffuse  *= attenuation;
    specular *= attenuation;
    
    return (ambient + diffuse + specular);
} 

spot light

struct SpotLight
{
    vec3 position;
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;

    float cutoff; //radian degree
    float outerCutoff; //radian degree
};
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);

//...

vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    
    // attenuation
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    
    // spotlight intensity
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutoff - light.outerCutoff;
    float intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
    
    // combine results
    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    ambient *= attenuation * intensity;
    diffuse *= attenuation * intensity;
    specular *= attenuation * intensity;
    
    return (ambient + diffuse + specular);
}

main에서 합치기

이제 합칠차례!!

그전에!!!

지금 각 Calc함수에서
각각 reflectDir을 계산하고
texture를 만들고 해서 추가하고 이러고 있는데
이건 최적화 하면됨 나중에 ㅇㅇ

전체 코드를 보면

#version 330 core
struct Material
{
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};

struct DirLight
{
    vec3 direction;
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

struct PointLight {
    vec3 position;

    float constant;
    float linear;
    float quadratic;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
#define NR_POINT_LIGHTS 4

struct SpotLight
{
    vec3 position;
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;

    float cutoff; //radian degree
    float outerCutoff; //radian degree
};

out vec4 FragColor;

in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords; //light map용 텍스쳐 좌표

uniform vec3 viewPos;
uniform Material mat;

uniform DirLight dirLight;
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLight;

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);

void main()
{
    // properties
    vec3 norm = normalize(Normal);
    vec3 viewDir = normalize(viewPos - FragPos);

    // phase 1: directional lighting
    vec3 result = CalcDirLight(dirLight, norm, viewDir);
    
    // phase 2: point lights
    for(int i = 0; i < NR_POINT_LIGHTS; i++)
    {
        result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);
    }
    
    // phase 3: spot light
    result += CalcSpotLight(spotLight, norm, FragPos, viewDir);

    FragColor = vec4(result, 1.0);
}

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    
    // combine results
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    
    return (ambient + diffuse + specular);
}

vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    
    // attenuation
    float distance    = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    
    // combine results
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    ambient  *= attenuation;
    diffuse  *= attenuation;
    specular *= attenuation;
    
    return (ambient + diffuse + specular);
}

vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    
    // attenuation
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    
    // spotlight intensity
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutoff - light.outerCutoff;
    float intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
    
    // combine results
    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    ambient *= attenuation * intensity;
    diffuse *= attenuation * intensity;
    specular *= attenuation * intensity;
    
    return (ambient + diffuse + specular);
}

코드 존나 간단해짐!

포인트 라이트에서 배열 선언한거 사용하려면 for루프나 뭐 다른 반복문 돌면됨!

glsl uniform 배열 사용법

그럼 이제 어떻게 씀?

그냥 쓰던것처럼 쓰면됨

uint32_t loc = glGetUniformLocation(shaderProgram, "someArrayUniform[0].someVariable");
glUniform1f(loc, 1.0f); 

그냥 똑같음ㅋㅋ

glm::vec3 pointLightPositions[] = 
    {
        glm::vec3( 0.7f,  0.2f,  2.0f),
        glm::vec3( 2.3f, -3.3f, -4.0f),
        glm::vec3(-4.0f,  2.0f, -12.0f),
        glm::vec3( 0.0f,  0.0f, -3.0f)
    }; 

대충 이런 점광 좌표를 설정해주고

	while (!glfwWindowShouldClose(window))
    {
        // 프레임 보정
        // ----------
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // 인풋
        // ----
        processInput(window);

        // 드로우 시작!
        // ----------
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        //cubeVAO에 사용할 쉐이더인 normalCubeShader프로그램을 GL에게 쉐이더 상태 등록
        normalCubeShader.use();
        normalCubeShader.setVec3("viewPos", camera.Position);
        normalCubeShader.setFloat("mat.shininess", 64.0f);
        //light 수정
        // directional light
        normalCubeShader.setVec3("dirLight.direction", -0.2f, -1.0f, -0.3f);
        normalCubeShader.setVec3("dirLight.ambient", 0.05f, 0.05f, 0.05f);
        normalCubeShader.setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f);
        normalCubeShader.setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f);
        // point light 1
        normalCubeShader.setVec3("pointLights[0].position", pointLightPositions[0]);
        normalCubeShader.setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f);
        normalCubeShader.setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f);
        normalCubeShader.setVec3("pointLights[0].specular", 1.0f, 1.0f, 1.0f);
        normalCubeShader.setFloat("pointLights[0].constant", 1.0f);
        normalCubeShader.setFloat("pointLights[0].linear", 0.09f);
        normalCubeShader.setFloat("pointLights[0].quadratic", 0.032f);
        // point light 2
        normalCubeShader.setVec3("pointLights[1].position", pointLightPositions[1]);
        normalCubeShader.setVec3("pointLights[1].ambient", 0.05f, 0.05f, 0.05f);
        normalCubeShader.setVec3("pointLights[1].diffuse", 0.8f, 0.8f, 0.8f);
        normalCubeShader.setVec3("pointLights[1].specular", 1.0f, 1.0f, 1.0f);
        normalCubeShader.setFloat("pointLights[1].constant", 1.0f);
        normalCubeShader.setFloat("pointLights[1].linear", 0.09f);
        normalCubeShader.setFloat("pointLights[1].quadratic", 0.032f);
        // point light 3
        normalCubeShader.setVec3("pointLights[2].position", pointLightPositions[2]);
        normalCubeShader.setVec3("pointLights[2].ambient", 0.05f, 0.05f, 0.05f);
        normalCubeShader.setVec3("pointLights[2].diffuse", 0.8f, 0.8f, 0.8f);
        normalCubeShader.setVec3("pointLights[2].specular", 1.0f, 1.0f, 1.0f);
        normalCubeShader.setFloat("pointLights[2].constant", 1.0f);
        normalCubeShader.setFloat("pointLights[2].linear", 0.09f);
        normalCubeShader.setFloat("pointLights[2].quadratic", 0.032f);
        // point light 4
        normalCubeShader.setVec3("pointLights[3].position", pointLightPositions[3]);
        normalCubeShader.setVec3("pointLights[3].ambient", 0.05f, 0.05f, 0.05f);
        normalCubeShader.setVec3("pointLights[3].diffuse", 0.8f, 0.8f, 0.8f);
        normalCubeShader.setVec3("pointLights[3].specular", 1.0f, 1.0f, 1.0f);
        normalCubeShader.setFloat("pointLights[3].constant", 1.0f);
        normalCubeShader.setFloat("pointLights[3].linear", 0.09f);
        normalCubeShader.setFloat("pointLights[3].quadratic", 0.032f);
        // spotLight
        normalCubeShader.setVec3("spotLight.position", camera.Position);
        normalCubeShader.setVec3("spotLight.direction", camera.Front);
        normalCubeShader.setVec3("spotLight.ambient", 0.0f, 0.0f, 0.0f);
        normalCubeShader.setVec3("spotLight.diffuse", 1.0f, 1.0f, 1.0f);
        normalCubeShader.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f);
        normalCubeShader.setFloat("spotLight.constant", 1.0f);
        normalCubeShader.setFloat("spotLight.linear", 0.09f);
        normalCubeShader.setFloat("spotLight.quadratic", 0.032f);
        normalCubeShader.setFloat("spotLight.cutoff", glm::cos(glm::radians(12.5f)));
        normalCubeShader.setFloat("spotLight.outerCutoff", glm::cos(glm::radians(15.0f))); 

        // world->view로 가는 view변환행렬/view->screen으로 가는 projection 변환 행렬 만들기
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        normalCubeShader.setMat4("projection", projection);
        normalCubeShader.setMat4("view", view);
        
        //bind texture
        //diffuse map
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, diffuseMap);
        //specular map
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, specularMap);
        
        glm::mat4 model = glm::mat4(1.0f);
        glBindVertexArray(cubeVAO);
        for(unsigned int i = 0; i < 10; i++)
        {
            model = glm::translate(model, cubePositions[i]);
            float angle = 20.0f * i;
            model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
            normalCubeShader.setMat4("model", model);

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        
        //lightCubeVAO에 사용할 쉐이더인 lightCubeShader프로그램을 GL에게 쉐이더 상태 등록
        lightCubeShader.use();
        //변환행렬들 생성
        lightCubeShader.setMat4("projection", projection);
        lightCubeShader.setMat4("view", view);

        glBindVertexArray(lightCubeVAO);
        for (unsigned int i = 0; i < 4; i++)
        {
            model = glm::mat4(1.0f);
            model = glm::translate(model, pointLightPositions[i]);
            model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
            lightCubeShader.setMat4("model", model);
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        
        glDrawArrays(GL_TRIANGLES, 0, 36);


        // glfw 버퍼 스와핑
        // ---------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

대충 요로코롬 main에서 사용해주면 됨

이렇게 잘 된당~

profile
그래픽스 하는 퍼그

0개의 댓글