본문 바로가기
Opengles 3.0 with Android

Chapter 5.1 Phong Shader

by SimonLee 2025. 3. 24.

 

 

퐁 셰이딩은 픽셀 단위에서 보간된 노말 벡터를 사용해 조명 계산을 수행하는 방식.
부드럽고 자연스러운 하이라이트 표현이 가능함.


Vertex Shading ( Gouraud ) VS Fragment Shading ( Phong ) 

  Vertex Shading ( Gouraud ) Fragment Shading (Phong)
조명 계산 위치 **정점(Vertex)**에서 조명 계산 픽셀(Fragment)**마다 조명 계산
보간(interpolation) 조명 결과(색상)를 보간 법선(Normal)**을 보간 → 픽셀에서 조명 계산
성능 빠름 (정점 수 적을수록 빠름) 느림 (픽셀마다 계산하니까 연산 많음)
품질 낮음 (하이라이트 등 디테일 손실 있음) 높음 (부드럽고 정확한 조명 효과)

 

버텍스 버퍼에 버텍스 포지션, 텍스처 좌표, 노말 3개의 값을 저장하여 어트리뷰트로 버텍스 셰이더로 넘겨주자.

stride          = (2 * sizeof(vec3) )+ sizeof(vec2) + sizeof(vec4);
offsetTexCoord  = ( GLvoid*) ( sizeof(glm::vec3) );
offset          = ( GLvoid*) ( sizeof(glm::vec3) + sizeof(vec2) );


// Create the VBO for our obj model vertices.
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, objMeshModel->vertices.size() * sizeof(objMeshModel->vertices[0]), &objMeshModel->vertices[0], GL_STATIC_DRAW);

// Create the VAO, this will store the vertex attributes into vectored state.
glGenVertexArrays(1, &OBJ_VAO_Id);
glBindVertexArray(OBJ_VAO_Id)
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glEnableVertexAttribArray(VERTEX_POSITION);
glEnableVertexAttribArray(TEX_COORD);
glEnableVertexAttribArray(NORMAL_POSITION);
glVertexAttribPointer(VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, stride, 0);
glVertexAttribPointer(TEX_COORD, 2, GL_FLOAT, GL_FALSE, stride, offsetTexCoord);
glVertexAttribPointer(NORMAL_POSITION, 3, GL_FLOAT, GL_FALSE, stride, offset);
glBindVertexArray(0);

 

 

 

필요한 계수 및 색상

종류 Uniform 이름 GLSL 타입 설정 값 설명
Material MaterialAmbient vec3 (0.1, 0.1, 0.1) 주변광 반사 계수 (ambient reflectivity)
Material MaterialSpecular vec3 (1.0, 0.5, 0.5) 정반사 반사 계수 (specular reflectivity)
Material MaterialDiffuse vec3 (0.75, 0.375, 0.0) 난반사 반사 계수 (diffuse reflectivity)
Material ShininessFactor float 40 광택 계수 (shininess)
Light LightAmbient vec3 (1.0, 1.0, 1.0) 광원의 주변광 (ambient light)
Light LightSpecular vec3 (1.0, 1.0, 1.0) 광원의 정반사 성분
Light LightDiffuse vec3 (1.0, 1.0, 1.0) 광원의 난반사 성분
  LightPosView (LightPosition) vec3 (0, 10, 0)  뷰 공간에서의 광원 위치

- Material : light의  반응 특성 ( 얼마나 밝아지는지, 하이라이트가 얼마나 보이는지 등 )

- Light : 광원의 색상과 세기

 

 

uniform을 사용하여 다음과 같이 색상값을 셰이더로 전달한다.

// -----------------------------
// Get Material property uniform variables
// -----------------------------
// Material의 Ambient 계수 uniform 위치 가져오기
char MaterialAmbient  = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"MaterialAmbient");
// Material의 Specular 계수 uniform 위치 가져오기
char MaterialSpecular = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"MaterialSpecular");
// Material의 Diffuse 계수 uniform 위치 가져오기
char MaterialDiffuse  = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"MaterialDiffuse");
// Material의 Shininess 계수 uniform 위치 가져오기
char ShininessFactor  = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"ShininessFactor");

// -----------------------------
// Get Light property uniform variables
// -----------------------------

// Light의 Ambient 계수 uniform 위치 가져오기
char LightAmbient     = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"LightAmbient");
// Light의 Specular 계수 uniform 위치 가져오기
char LightSpecular    = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"LightSpecular");
// Light의 Diffuse 계수 uniform 위치 가져오기
char LightDiffuse     = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"LightDiffuse");
// 뷰 공간 기준 Light 위치 uniform 위치 가져오기
char LightPosition    = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"LightPosView");

// -----------------------------
// Set Material uniform values
// -----------------------------
if (MaterialAmbient >= 0) {
    LOGI("MaterialAmbient Setting");
    // 주변광 반사 계수: 어두운 회색
    glUniform3f(MaterialAmbient, 0.1f, 0.1f, 0.1f);
}
if (MaterialSpecular >= 0) {
    LOGI("MaterialSpecular Setting");
    // 정반사 반사 계수: 빨간 기가 도는 흰색
    glUniform3f(MaterialSpecular, 1.0f, 0.5f, 0.5f);
}
....

 

 

Vertex Shader Code

#version 300 es

// 정점 속성
layout(location = 0) in vec4 VertexPosition;   // 모델 공간의 정점 위치
layout(location = 1) in vec3 Normal;           // 모델 공간의 노멀 벡터

// 변환 매트릭스
uniform mat4 ModelViewProjectionMatrix;  // 모델 → 뷰 → 프로젝션 변환 행렬 (최종 화면 좌표 계산용)
uniform mat4 ModelViewMatrix;            // 모델 → 뷰 변환 행렬 (카메라 공간에서 좌표계 변환용)
uniform mat3 NormalMatrix;               // 노멀 벡터를 변환하기 위한 행렬 (ModelViewMatrix의 상단 3x3 역전치)

// 프래그먼트 셰이더로 넘겨줄 변수 (조명 계산용)
out vec3 normalByView;   // 뷰(카메라) 공간에서의 노멀 벡터
out vec3 vertexByView;   // 뷰(카메라) 공간에서의 정점 위치

void main() 
{
    // 모델 공간의 노멀 벡터를 뷰 공간으로 변환
    normalByView = NormalMatrix * Normal;

    // 모델 공간의 정점 위치를 뷰 공간으로 변환
    vertexByView = vec3(ModelViewMatrix * VertexPosition);

    // 최종 클립 공간 위치 계산 (뷰 → 프로젝션 → NDC → 화면)
    gl_Position = ModelViewProjectionMatrix * VertexPosition;
}

 

Fragment Shader Code

#version 300 es
precision mediump float;

// ---------------------------
// Uniforms: 외부에서 전달되는 Material & Light 속성
// ---------------------------

// Material (재질) 속성
uniform vec3 MaterialAmbient;
uniform vec3 MaterialSpecular;
uniform vec3 MaterialDiffuse;
uniform float ShininessFactor; // 광택 계수

// Light (조명) 속성
uniform vec3 LightAmbient;
uniform vec3 LightSpecular;
uniform vec3 LightDiffuse;
uniform vec3 LightPosition; // 뷰 공간 기준 광원 위치

// ---------------------------
// Inputs: 정점 셰이더에서 전달받는 뷰 좌표계 기준 벡터들
// ---------------------------
in vec3 normalByView;     // 뷰 공간 기준 정점 노멀
in vec3 vertexByView;     // 뷰 공간 기준 정점 위치

// ---------------------------
// Output: 픽셀 색상
// ---------------------------
layout(location = 0) out vec4 FinalColor;

// ---------------------------
// Phong Shading 함수 (ADS 모델)
// ---------------------------
vec3 PhongShading()
{
    // 정규화된 벡터 계산
    vec3 N = normalize(normalByView); // 노멀 벡터
    vec3 V = normalize(-vertexByView); // 뷰어 벡터 (카메라는 원점에 있으므로)
    vec3 L = normalize(LightPosition - vertexByView); // 광원 벡터

    // Diffuse: Lambert law (N ⋅ L)
    float diffIntensity = max(dot(N, L), 0.0);

    // Specular: 반사벡터 R = reflect(-L, N), 시선벡터 V
    vec3 R = reflect(-L, N);
    float specIntensity = pow(max(dot(R, V), 0.0), ShininessFactor);

    // 조명 각 성분 계산 (Material * Light)
    vec3 ambient  = MaterialAmbient  * LightAmbient;
    vec3 diffuse  = MaterialDiffuse  * LightDiffuse * diffIntensity;
    vec3 specular = MaterialSpecular * LightSpecular * specIntensity;

    // 최종 색상 합산
    return ambient + diffuse + specular;
}

// ---------------------------
// Fragment Shader main()
// ---------------------------
void main() {
    FinalColor = vec4(PhongShading(), 1.0); // 알파는 항상 1.0
}

'Opengles 3.0 with Android' 카테고리의 다른 글

Chapter 6.0 Warble Effect  (0) 2025.04.16
Chapter 5.2 Multi Light  (0) 2025.04.15
Chapter 5.0 Vertex lighting  (0) 2025.03.24
Chapter 4. OBJ Loader  (0) 2025.03.08
Chapter 3.3 Primitive Restart < Skip >  (2) 2025.02.24