Lighting

대인공·2022년 8월 15일
0

OpenGL

목록 보기
8/8
post-thumbnail
  • 물체 표면의 색상을 결정하는 과정
    • 광원(light source)
    • 재질(material)


      위 두가지를 거쳐 눈에 색이 도달하는 과정 (매우 복잡한 물리적 현상)

Illumination Model

  • Local / Global 두가지 모델이 있다.
    - 반사광 : 광원이 물체에 반사되어 발생한 빛
    - Local : 반사광을 고려하지 않는다.
    - Global : 반사광을 고려한다. (더 사실적, 높은 계산 비용)

Phong's Illumination Model

  • 가장 대표적인 Local illumination model
    - 빛과 물체간의 색상 결정을 3가지로 나눠서 표현
    - ambient(주변광), diffuse(분산광), specular(반사광) 이 세가지의 빛을 더하여 최종 색상을 결정

  • Ambient Light
    - 주변광
    - 빛의 방향, 물체 표변의 방향, 시선 방향과 아무 상관 없이 물체가 기본적으로 받는 빛
    - 상수값으로 처리한다.

  • Diffuse Light
    - 분산광
    - 빛이 물체 표면에 부딪혔을 때, 모든 방향으로 고르게 퍼지는 빛, 즉 시선의 방향과는 상관없이 빛의 방향과 물체 표면의 방향에 따라 결정된다.
    - Diffuse = dot(ligth, normal)


  • Specular Light
    - 반사광
    - 빛이 물체 표면에 부딪혀 반사되는 광원
    - 시선의 방향과 반사된 빛의 방향이 동일할때의 빛이 가장 강한 빛이다.
    - Reflect = 2 * dot(light, normal) - light
    - Speular = dot(view, reflect)


Ambient Light 적용

  • vertex shader 추가 , shader / lighting.vs 작성
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;

uniform mat4 transform;

out vec3 normal;
out vec2 texCoord;

void main() {
  gl_Position = transform * vec4(aPos, 1.0);
  normal = aNormal;
  texCoord = aTexCoord;
}

  • fragment shader 추가 , shader / lighting.fs 작성
#version 330 core
in vec3 normal;
in vec2 texCoord;
out vec4 fragColor;

uniform vec3 lightColor;
uniform vec3 objectColor;
uniform float ambientStrength;

void main() {
  vec3 ambient = ambientStrength * lightColor;
  vec3 result = ambient * objectColor;
  fragColor = vec4(result, 1.0);
}
  • lightColor로 빛의 색상을, objectColor로 물체의 색상을 나타낸다.
  • ambientStrength만큼 전체 밝기를 높여준다
vec3 ambient = ambientStrength * lightColor;

  • 새로운 shader로드, src / Context.cpp / Init() 작성
ShaderPtr vertShader = Shader::CreateFromFile("./shader/lighting.vs", GL_VERTEX_SHADER);
ShaderPtr fragShader = Shader::CreateFromFile("./shader/lighting.fs", GL_FRAGMENT_SHADER);

  • float, int 타입의 uniform설정 함수 추가 , src / program.h 작성
	void SetUniform(const std::string& name, int value) const;
 	void SetUniform(const std::string& name, float value) const; // 추가된 항목
  	void SetUniform(const std::string& name, const glm::vec3& value) const; // 추가된 항목
  	void SetUniform(const std::string& name, const glm::mat4& value) const;

  • src / program.cpp 작성
void Program::SetUniform(const std::string& name, float value) const {
    auto loc = glGetUniformLocation(m_program, name.c_str());
    glUniform1f(loc, value);
}

void Program::SetUniform(const std::string& name, const glm::vec3& value) const {
    auto loc = glGetUniformLocation(m_program, name.c_str());
    glUniform3fv(loc, 1, glm::value_ptr(value));
}

  • lightColor와 objectColor 저장을 위한 맴버 추가 , src / context.h 작성
	// light parameter
  	glm::vec3 m_lightColor { glm::vec3(1.0f, 1.0f, 1.0f) };
  	glm::vec3 m_objectColor { glm::vec3(1.0f, 0.5f, 0.0f) };
  	float m_ambientStrength { 0.1f };

  • ImGui를 사용하여 파라미터를 수정하기 위한 코드 추가 , src / context.cpp / Render() 작성
if (ImGui::Begin("ui window")) 
{
  	if (ImGui::CollapsingHeader("light")) 
    {
    	ImGui::ColorEdit3("light color", glm::value_ptr(m_lightColor));
    	ImGui::ColorEdit3("object color", glm::value_ptr(m_objectColor));
    	ImGui::SliderFloat("ambient strength", &m_ambientStrength, 0.0f, 1.0f);
  	}
//...

  • uniform 적용 , src / context.cpp / Render() 작성
ImGui::End();
 
m_program->Use(); // 추가된 항목
m_program->SetUniform("lightColor", m_lightColor); // 추가된 항목
m_program->SetUniform("objectColor", m_objectColor); // 추가된 항목
m_program->SetUniform("ambientStrength", m_ambientStrength); // 추가된 항목

Diffuse Light 적용

  • 분산광의 밝기를 결정하는 요소
    - 빛의 방향과 밝기
    - 표면의 normal vector 방향

  • 큐브의 정점 데이터에 normal추가 , src / context.cpp / Init() 작성
    - 순서 : pos.xyz, normal.xyz, texcoord.uv
float vertices[] = {
  -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f,
   0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 1.0f, 0.0f,
   0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f,
  -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 0.0f, 1.0f,

  -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f,
   0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f, 1.0f, 0.0f,
   0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f,
  -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f, 0.0f, 1.0f,

  -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f,
  -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f, 1.0f, 1.0f,
  -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f,
  -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f, 0.0f, 0.0f,

   0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f,
   0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f, 1.0f, 1.0f,
   0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f,
   0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f, 0.0f, 0.0f,

  -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f,
   0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f, 1.0f, 1.0f,
   0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f,
  -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f, 0.0f, 0.0f,

  -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f,
   0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f, 1.0f, 1.0f,
   0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f,
  -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f, 0.0f, 0.0f,
};

  • VAO, VBO 수정 , src / context.cpp / Init() 작성
    - 늘어난 attribute에 따라 수정
m_vertexBuffer = Buffer::CreateWithData(
    GL_ARRAY_BUFFER, GL_STATIC_DRAW,
    vertices, sizeof(float) * 8 * 6 * 4);

m_vertexLayout->SetAttrib(0, 3, GL_FLOAT, GL_FALSE,
  sizeof(float) * 8, 0);
m_vertexLayout->SetAttrib(1, 3, GL_FLOAT, GL_FALSE,
  sizeof(float) * 8, sizeof(float) * 3);
m_vertexLayout->SetAttrib(2, 2, GL_FLOAT, GL_FALSE,
  sizeof(float) * 8, sizeof(float) * 6);
  • 코드 수정 , shader / lighting.vs 작성
    - 추가된 attribute에 따라 수정
layout (location = 2) in vec2 aTexCoord;
 
uniform mat4 transform;
uniform mat4 modelTransform; // 추가된 항목
 
out vec3 normal; // 추가된 항목
out vec2 texCoord;
out vec3 position; // 추가된 항목
 
void main() {
  gl_Position = transform * vec4(aPos, 1.0);
  normal = (transpose(inverse(modelTransform)) * vec4(aNormal, 0.0)).xyz; // 추가된 항목
  texCoord = aTexCoord;
  position = (modelTransform * vec4(aPos, 1.0)).xyz; // 추가된 항목
}
  • gl_Position외에도 position을 계산하여 fragment shader에 넘기는 이유
    - gl_Position은 perspective transform이 적용되어 canonical space상의 좌표값으로 전환됨
    - diffuse값을 계산하려면 world space상에서의 좌표값이 필요
    - normal도 같은 이유로 model transform만 적용

  • normal에 modelTransform의 inverse transform을 적용하는 이유
    - 점이 아닌 벡터의 경우 이렇게 해야 제대로 변환된 값을 계산할 수 있다.
    - matrix의 inverse transform은 모든 점에서 동일하므로 보통 별도의 uniform으로 입력한다.
    normal = (transpose(inverse(modelTransform)) * vec4(aNormal, 0.0)).xyz;

    보통 이 코드는 uniform mat4 inverseTransModelTransform; 이라고 별도의 uniform을 만들어 입력해 주는데 이 이유는 위와 같은 방식으로 할 경우 컴퓨터기 일일히 계산해야 하므로써 연산에 부담이 증가하게 된다. 어짜피 계산의 결과는 같으므로 별도의 uniform을 만들어 값을 바로 대입시켜주는 것이다.


  • 코드 수정 , src / lighting.fs 작성
#version 330 core
in vec3 normal;
in vec2 texCoord;
in vec3 position; // 추가된 항목
out vec4 fragColor;
 
uniform vec3 lightPos; // 추가된 항목
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform float ambientStrength;
 
void main() {
    vec3 ambient = ambientStrength * lightColor;
    
    //빛의 방향
    vec3 lightDir = normalize(lightPos - position); // 추가된 항목
    
    // 법선 방향
    vec3 pixelNorm = normalize(normal); // 추가된 항목
    
    // -(마이너스)이하의 값은 필요가 없기 때문에 max() 사용
    // 그 값에 빛의 색(lightColor)을 곱해준다.
    vec3 diffuse = max(dot(pixelNorm, lightDir), 0.0) * lightColor; // 추가된 항목
    
    vec3 result = (ambient + diffuse) * objectColor; // 추가된 항목
    fragColor = vec4(result, 1.0);
}
  • lightDir = normalize(lightPos - position);
    - (빛의 방향) = (광원의 위치) - (각 픽셀의 3D위치);
  • normal을 다시 normalize 하는 이유
    - vertex shader에서 계산된 normal은 rasterization되는 과정에서 선형 보간이 진행됨
    - unit vector간의 선형 보간 결과는 unit vector보장을 못하기 때문에 normalization 해주어야 한다.

  • 추가된 light position관련 맴버 변수 추가 , src / context.h 작성
    - light의 위치에 따라 변하는 표면 색상을 관찰하기 위해 애니메이션 멈춤기능도 추가
  // animation
  bool m_animation { true };
 
  // clear color
  glm::vec4 m_clearColor { glm::vec4(0.1f, 0.2f, 0.3f, 0.0f) };
 
  // light parameter
  glm::vec3 m_lightPos { glm::vec3(3.0f, 3.0f, 3.0f) };
  glm::vec3 m_lightColor { glm::vec3(1.0f, 1.0f, 1.0f) };
  glm::vec3 m_objectColor { glm::vec3(1.0f, 0.5f, 0.0f) };
  float m_ambientStrength { 0.1f };

  • 광원위치 확인과 애니메이션 On/Off추가 , src / context.cpp / Render() 작성
if (ImGui::Begin("ui window")) 
{
  if (ImGui::CollapsingHeader("light", ImGuiTreeNodeFlags_DefaultOpen)) // 추가된 항목
  {
    ImGui::DragFloat3("light pos", glm::value_ptr(m_lightPos), 0.01f); // 추가된 항목
    ImGui::ColorEdit3("light color", glm::value_ptr(m_lightColor));
    ImGui::ColorEdit3("object color", glm::value_ptr(m_objectColor));
    ImGui::SliderFloat("ambient strength", &m_ambientStrength, 0.0f, 1.0f);
  }
 
  ImGui::Checkbox("animation", &m_animation); // 추가된 항목
 
  if (ImGui::ColorEdit4("clear color", glm::value_ptr(m_clearColor))) 
  {
    glClearColor(m_clearColor.r, m_clearColor.g, m_clearColor.b, m_clearColor.a);
  }

  • uniform 입력
    - m_animation 값에 따라 회전 애니메이션 적용 , src / context.cpp / Render() 작성
	m_program->SetUniform("lightPos", m_lightPos); // 추가된 항목
m_program->SetUniform("lightColor", m_lightColor);
m_program->SetUniform("objectColor", m_objectColor);
m_program->SetUniform("ambientStrength", m_ambientStrength);
for (size_t i = 0; i < cubePositions.size(); i++){
  auto& pos = cubePositions[i];
  auto model = glm::translate(glm::mat4(1.0f), pos);
  
  auto angle = glm::radians((float)glfwGetTime() * 120.0f + 20.0f * (float)i); // 추가된 항목
  model = glm::rotate(model, m_animation ? angle : 0.0f, glm::vec3(1.0f, 0.5f, 0.0f)); // 추가된 항목
    
  auto transform = projection * view * model;
  m_program->SetUniform("transform", transform);
  m_program->SetUniform("modelTransform", model); // 추가된 항목
  glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}

  • 테스트 큐브 그리기 , src / context.cpp / Render() 작성
auto lightModelTransform =
glm::translate(glm::mat4(1.0), m_lightPos) *
glm::scale(glm::mat4(1.0), glm::vec3(0.1f));
m_program->Use();
m_program->SetUniform("lightPos", m_lightPos);
m_program->SetUniform("lightColor", glm::vec3(1.0f, 1.0f, 1.0f));
m_program->SetUniform("objectColor", glm::vec3(1.0f, 1.0f, 1.0f));
m_program->SetUniform("ambientStrength", 1.0f);
m_program->SetUniform("transform", projection * view * lightModelTransform);
m_program->SetUniform("modelTransform", lightModelTransform);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

  • 결과
    - 광원의 위치에 따라 명암이 생기는것을 관찰할 수 있다.





Specular Light

  • 반사광의 밝기를 결정하는 요소
    - 빛의 방향과 밝기
    - 표면의 normal vector 방향
    - 시선의 방향 (카메라의 위치, 카메라가 바라보고있는 방향을 뜻함)

  • 기존의 카메라의 위치와 방향을 가져와 계산식에 추가 , shader / lighting.fs 작성
#version 330 core
in vec3 normal;
in vec2 texCoord;
in vec3 position;
out vec4 fragColor;
 
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform float ambientStrength;
uniform float specularStrength; // 추가된 항목
uniform float specularShininess; // 추가된 항목
uniform vec3 viewPos; // 추가된 항목
 
void main() {
    vec3 ambient = ambientStrength * lightColor;
 
    vec3 lightDir = normalize(lightPos - position);
    vec3 pixelNorm = normalize(normal);
    vec3 diffuse = max(dot(pixelNorm, lightDir), 0.0) * lightColor;
 
    vec3 viewDir = normalize(viewPos - position); // 추가된 항목
    vec3 reflectDir = reflect(-lightDir, pixelNorm); // 추가된 항목
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), specularShininess); // 추가된 항목
    vec3 specular = specularStrength * spec * lightColor; // 추가된 항목
 
    vec3 result = (ambient + diffuse + specular) * objectColor; // 추가된 항목
    fragColor = vec4(result, 1.0);
}

reflect(light, normal) : light벡터 방향의 광선이 normal벡터 방향의 표면에 부딪혔을 때 반사되는 벡터를 출력하는 OpenGL내장 함수이다. (입사하는 방향벡터와 픽셀, 면의 Normal Vector를 주면 반사되는 방향벡터를 리턴)

  • 현재 카메라의 world space상의 좌표와 픽셀 좌표간의 차를 통해 viewDir(시선벡터)을 계산한다.
  • spec : reflectDir과 viewDir간의 내적을 통해 반사광을 많이 보는 정도를 계산, 0~1 사이의 값을 리턴한다.
  • specularStrength : 반사광의 정도를 조절
  • specularShininess : 반사광의 면적을 조절
    • specularShininess 값에 따른 specular 하이라이트의 변화


  • 반사광 제어를 위한 파라미터 추가 , src / context.h 작성
// light parameter
glm::vec3 m_lightPos { glm::vec3(3.0f, 3.0f, 3.0f) };
glm::vec3 m_lightColor { glm::vec3(1.0f, 1.0f, 1.0f) };
glm::vec3 m_objectColor { glm::vec3(1.0f, 0.5f, 0.0f) };
float m_ambientStrength { 0.1f };
float m_specularStrength { 0.5f }; // 추가된 항목
float m_specularShininess { 32.0f }; // 추가된 항목

  • 추가된 uniform 세팅 , src / context.cpp / Render() 작성
ImGui::SliderFloat("ambient strength", &m_ambientStrength, 0.0f, 1.0f);
ImGui::SliderFloat("specular strength", &m_specularStrength, 0.0f, 1.0f); // 추가된 항목
ImGui::DragFloat("specular shininess", &m_specularShininess, 1.0f, 1.0f, 256.0f); // 추가된 항목
m_program->SetUniform("viewPos", m_cameraPos); // 추가된 항목
m_program->SetUniform("lightPos", m_lightPos);
m_program->SetUniform("lightColor", m_lightColor);
m_program->SetUniform("objectColor", m_objectColor);
m_program->SetUniform("ambientStrength", m_ambientStrength);
m_program->SetUniform("specularStrength", m_specularStrength); // 추가된 항목
m_program->SetUniform("specularShininess", m_specularShininess); // 추가된 항목

  • 결과
    - 광원의 위치에 따라 오브젝트에 반사된 빛의 세기가 다르게 보인다.





Material

  • 최종 색상 = 빛 색상 * 재질의 색상
  • 재질(Material)을 ambient / diffuse / specular로 나누어 표현하면
    - 금속 재질은 반사광이 강하다
    - 면직 재질은 반사광이 약하다

  • 구조체를 만들어 한곳에 모아둔다 , shader / lighting.fs 작성
#version 330 core
in vec3 normal;
in vec2 texCoord;
in vec3 position;
out vec4 fragColor;
 
uniform vec3 viewPos;
 
struct Light 
{
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform Light light;
 
struct Material 
{
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};
uniform Material material;
 
void main() {
    vec3 ambient = material.ambient * light.ambient;
 
    vec3 lightDir = normalize(light.position - position);
    vec3 pixelNorm = normalize(normal);
    float diff = max(dot(pixelNorm, lightDir), 0.0);
    vec3 diffuse = diff * material.diffuse * light.diffuse;
 
    vec3 viewDir = normalize(viewPos - position);
    vec3 reflectDir = reflect(-lightDir, pixelNorm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = spec * material.specular * light.specular;
 
    vec3 result = ambient + diffuse + specular;
    fragColor = vec4(result, 1.0);
}

  • light, Material 파라미터 수정 , src / context.h 작성
// light parameter
struct Light {
  glm::vec3 position { glm::vec3(3.0f, 3.0f, 3.0f) };
  glm::vec3 ambient { glm::vec3(0.1f, 0.1f, 0.1f) };
  glm::vec3 diffuse { glm::vec3(0.5f, 0.5f, 0.5f) };
  glm::vec3 specular { glm::vec3(1.0f, 1.0f, 1.0f) };
};
Light m_light;

// material parameter
struct Material {
  glm::vec3 ambient { glm::vec3(1.0f, 0.5f, 0.3f) };
  glm::vec3 diffuse { glm::vec3(1.0f, 0.5f, 0.3f) };
  glm::vec3 specular { glm::vec3(0.5f, 0.5f, 0.5f) };
  float shininess { 32.0f };
};
Material m_material;

  • 파라미터 UI 수정 , src / context.cpp / Render() 작성
if (ImGui::Begin("ui window")) {
  if (ImGui::CollapsingHeader("light", ImGuiTreeNodeFlags_DefaultOpen))  // 추가된 항목
  {
      ImGui::DragFloat3("l.position", glm::value_ptr(m_light.position), 0.01f); // 추가된 항목
      ImGui::ColorEdit3("l.ambient", glm::value_ptr(m_light.ambient)); // 추가된 항목
      ImGui::ColorEdit3("l.diffuse", glm::value_ptr(m_light.diffuse)); // 추가된 항목
      ImGui::ColorEdit3("l.specular", glm::value_ptr(m_light.specular)); // 추가된 항목
  }
 
  if (ImGui::CollapsingHeader("material", ImGuiTreeNodeFlags_DefaultOpen))  // 추가된 항목
  {
      ImGui::ColorEdit3("m.ambient", glm::value_ptr(m_material.ambient)); // 추가된 항목
      ImGui::ColorEdit3("m.diffuse", glm::value_ptr(m_material.diffuse)); // 추가된 항목
      ImGui::ColorEdit3("m.specular", glm::value_ptr(m_material.specular)); // 추가된 항목
      ImGui::DragFloat("m.shininess", &m_material.shininess, 1.0f, 1.0f, 256.0f); // 추가된 항목
  }
//...
}

  • uniform 적용 코드 수정 , src / context.cpp / Render() 작성
m_program->SetUniform("viewPos", m_cameraPos);
m_program->SetUniform("light.position", m_light.position); // 추가된 항목
m_program->SetUniform("light.ambient", m_light.ambient); // 추가된 항목
m_program->SetUniform("light.diffuse", m_light.diffuse); // 추가된 항목
m_program->SetUniform("light.specular", m_light.specular); // 추가된 항목
m_program->SetUniform("material.ambient", m_material.ambient); // 추가된 항목
m_program->SetUniform("material.diffuse", m_material.diffuse); // 추가된 항목
m_program->SetUniform("material.specular", m_material.specular); // 추가된 항목
m_program->SetUniform("material.shininess", m_material.shininess); // 추가된 항목

  • 결과
    - 재질에 따른 반사광이 다르다는 것을 알 수 있다.





Lighting Map (texture map)

  • 재질을 구성하는 ambient, diffuse, specular모두 텍스쳐 맵으로 대체 가능하다.
  • 흔히 사용하는 오브젝트의 texture map은 결국 diffuse map으로 사용

Refactoring

  • 이전 코드들을 리펙토링해준다.

  • 정리 , src / context.h 작성

CLASS_PTR(Program)
class Program {
public:
 	static ProgramUPtr Create(const std::vector<ShaderPtr>& shaders);
  
    static ProgramUPtr Create(const std::string& vertShaderFilename, const std::string& fragShaderFilename);
 
  // ...
  void SetUniform(const std::string& name, const glm::vec4& value) const;
  // ...

  • 정리 , src / context.cpp 작성
ProgramUPtr Program::Create(const std::string& vertShaderFilename, const std::string& fragShaderFilename) 
{
  	ShaderPtr vs = Shader::CreateFromFile(vertShaderFilename, GL_VERTEX_SHADER);
  	ShaderPtr fs = Shader::CreateFromFile(fragShaderFilename, GL_FRAGMENT_SHADER);
  	if (!vs || !fs)
    	return nullptr;
  	return std::move(Create({vs, fs}));
}

void Program::SetUniform(const std::string& name, const glm::vec4& value) const 
{
  	auto loc = glGetUniformLocation(m_program, name.c_str());
  	glUniform4fv(loc, 1, glm::value_ptr(value));
}

  • 맴버 변수 추가 , src / context.h 작성
private:
    Context() {}
    bool Init();
    ProgramUPtr m_program;
    ProgramUPtr m_simpleProgram; // 추가된 항목

  • program 초기화 , src / context.cpp / Init() 작성
m_simpleProgram = Program::Create("./shader/simple.vs", "./shader/simple.fs");
if (!m_simpleProgram)
  return false;

m_program = Program::Create("./shader/lighting.vs", "./shader/lighting.fs");
if (!m_program)
  return false;

  • shader 수정 , shader / simple.vs 작성
#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 transform;

void main() {
    gl_Position = transform * vec4(aPos, 1.0);
}

  • shader 수정 , shader / simple.fs 작성
#version 330 core
uniform vec4 color;
out vec4 fragColor;

void main() {
    fragColor = color;
}

  • 광원의 위치를 그리는 코드를 수정 , src / context.cpp / Render() 작성
auto lightModelTransform = glm::translate(glm::mat4(1.0), m_light.position) * glm::scale(glm::mat4(1.0), glm::vec3(0.1f));
	m_simpleProgram->Use(); // 추가된 항목
	m_simpleProgram->SetUniform("color", glm::vec4(m_light.ambient + m_light.diffuse, 1.0f)); // 추가된 항목
	m_simpleProgram->SetUniform("transform", projection * view * lightModelTransform);
	glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0); // 추가된 항목
 
	m_program->Use(); // 추가된 항목

diffuse map

  • lighting 수정 , shader / lighting.fs 작성
struct Material {
    sampler2D diffuse; // 추가된 항목
    vec3 specular;
    float shininess;
};
uniform Material material;
 
void main() {
  vec3 texColor = texture2D(material.diffuse, texCoord).xyz; // 추가된 항목
  vec3 ambient = texColor * light.ambient; // 추가된 항목
 
  vec3 lightDir = normalize(light.position - position);
  vec3 pixelNorm = normalize(normal);
  float diff = max(dot(pixelNorm, lightDir), 0.0);
  vec3 diffuse = diff * texColor * light.diffuse; // 추가된 항목

  • Material 구조체 수정 , src / context.h 작성
  // material parameter
  struct Material {
      TextureUPtr diffuse; // 추가된 항목
      glm::vec3 specular { glm::vec3(0.5f, 0.5f, 0.5f) };
      float shininess { 32.0f };
  };
  Material m_material;

  • diffuse 텍스처 로딩 , src / context.cpp / Init() 작성
m_material.diffuse = Texture::CreateFromImage(Image::Load("./image/container2.png").get());

  • uniform 설정 , src / context.cpp / Render() 작성
m_program->Use();
m_program->SetUniform("viewPos", m_cameraPos);
m_program->SetUniform("light.position", m_light.position);
m_program->SetUniform("light.ambient", m_light.ambient);
m_program->SetUniform("light.diffuse", m_light.diffuse);
m_program->SetUniform("light.specular", m_light.specular);
m_program->SetUniform("material.diffuse", 0); // 추가된 항목
m_program->SetUniform("material.specular", m_material.specular);
m_program->SetUniform("material.shininess", m_material.shininess);
 
glActiveTexture(GL_TEXTURE0); // 추가된 항목
m_material.diffuse->Bind(); // 추가된 항목

  • 결과
    - 명암이 들어간 텍스쳐
    - 다른 재질임에도 동일하게 하이라이트가 발생하는 것을 볼수 있다.

동일한 형태의 specular를 적용하고 있기 때문에 생기는 문제이다.
- specular color도 texture map으로 대체하면 해결된다.

spceular map

  • spceular map을 읽어오고 적용하도록 코드 수정

  • 수정 , shader / lighting.fs 작성

struct Material {
  sampler2D diffuse;
  sampler2D specular; // 추가된 항목
  float shininess;
};

void main()
{
	//...
	vec3 specColor = texture2D(material.specular, texCoord).xyz; // 추가된 항목
	vec3 viewDir = normalize(viewPos - position);
	vec3 reflectDir = reflect(-lightDir, pixelNorm);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
	vec3 specular = spec * specColor * light.specular; // 추가된 항목
    //...
}

  • 수정 , src / context.h 작성
// ... in Context declaration
struct Material {
  TextureUPtr diffuse;
  TextureUPtr specular; // 추가된 항목
  float shininess { 32.0f };
};

  • 수정 , src / context.cpp / Init() 작성
m_material.diffuse = Texture::CreateFromImage(Image::Load("./image/container2.png").get());
 
m_material.specular = Texture::CreateFromImage(Image::Load("./image/container2_specular.png").get()); // 추가된 항목
  • 수정 , src / context.cpp / Render() 작성
m_program->Use();
m_program->SetUniform("viewPos", m_cameraPos);
m_program->SetUniform("light.position", m_light.position);
m_program->SetUniform("light.ambient", m_light.ambient);
m_program->SetUniform("light.diffuse", m_light.diffuse);
m_program->SetUniform("light.specular", m_light.specular);
m_program->SetUniform("material.diffuse", 0);
m_program->SetUniform("material.specular", 1); // 추가된 항목
m_program->SetUniform("material.shininess", m_material.shininess);
 
glActiveTexture(GL_TEXTURE0);
m_material.diffuse->Bind();
glActiveTexture(GL_TEXTURE1); // 추가된 항목
m_material.specular->Bind(); // 추가된 항목
  • 결과
    - 재질에 따라 발생하는 하이라이트가 다르게 적용되는 것을 볼수 있다.

profile
이제 막 시작하는 유니티 클라이언트

0개의 댓글

관련 채용 정보