OpenGL Shader – 32

GLSL 예제 : Texture(MultiTexture) – 3/3

원문 : http://www.lighthouse3d.com/opengl/glsl/index.php?textureMulti

멀티 텍스쳐링은 GLSL에서는 정말 쉽다. 해야할 것은 두개의 텍스쳐에 접근하는 일이 전부다. 그리고 이 장에서는 동일한 텍스쳐 좌표를 사용할 것이기 때문에 버텍스 쉐이더를 재작성하지 않고 이전 강좌의 것을 그대로 사용한다. 프레그먼트 쉐이더 또한 두개의 텍스쳐 색상을 추가하는 최소한의 변화만 있다.

varying vec3 lightDir,normal;
uniform sampler2D tex,l3d;

void main()
{
    vec3 ct,cf;
    vec4 texel;
    float intensity,at,af;
		
    intensity = max(dot(lightDir,normalize(normal)),0.0);
	
    cf = intensity * (gl_FrontMaterial.diffuse).rgb + 
         gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;
	
    texel = texture2D(tex,gl_TexCoord[0].st)+
            texture2D(l3d,gl_TexCoord[0].st);

    ct = texel.rgb;
    at = texel.a;
	
    gl_FragColor = vec4(ct * cf, at * af);	
}

그리고 이제, 약간 다른 특별한 것을 해보자: Glow in the Drak Effect. 이 특별한 효과는 두번째 텍스쳐를 어둠속에서 타오르는듯하게 보이는 것이다. 예를들어, 완전이 밝아졌다가 어두워지는 듯한 효과.
두 단계로 최종 색상을 다시 계산해야 하는데, 먼저 첫번째 텍스쳐를 프레그먼트 색상으로 조정된 상태의 색상을 계산하고, 이후에 두번째 텍스쳐 유닛을 강도에 기반해서 추가하는 것이다.

intensity가 0이라면, 두번째 텍스쳐가 전체적으로 완전하게 표시된다. 반대로 intensity가 1이라면, 두번째 텍스쳐를 10% 정도만 표시되도록 한다. 이런 표시를 위해 smoothStep라는 함수를 통해 구현할 것이다. 이 함수의 시그니쳐는 다음과 같다.

genType smoothStep(genType edge0, genType edge1, genType x);

만약 x<=edge0이면 결과는 0이고,  x>=edge1이면 1 그리고 edge0< x <edge1이면 Hermit 보간 연산이 수행된다. 우리의 경우 다음 함수를 호출한다.

coef = smoothStep(1.0, 0.2, intensity);

다음은 프레그먼트 쉐이더 코드이다.

varying vec3 lightDir,normal;
uniform sampler2D tex,l3d;

void main()
{
    vec3 ct,cf,c;
    vec4 texel;
    float intensity,at,af,a;
	
    intensity = max(dot(lightDir,normalize(normal)),0.0);
	
    cf = intensity * (gl_FrontMaterial.diffuse).rgb + 
             gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;
	
    texel = texture2D(tex,gl_TexCoord[0].st);
		
    ct = texel.rgb;
    at = texel.a;
	
    c = cf * ct;
    a = af * at;
		
    float coef = smoothstep(1.0,0.2,intensity);
    c += coef *  vec3(texture2D(l3d,gl_TexCoord[0].st));
	
    gl_FragColor = vec4(c, a);	
}

“OpenGL Shader – 32”에 대한 18개의 댓글

  1. 급하게 OpenGL을 익혀야되는 상황에 김형준님 블로그 OpenGL강의가 많이 도움이 되었습니다. 그리하여 본격적으로 OpenGL ES를 공부해야되서 서점에가서 책을 샀더니 김형준님께서 번역하신거더라구요. 한 10일동안 김형준님의 작품속에서 시간을 보냈네요. 감사합니다^0^

    1. 저도 여기서 쉽게 OpenGL에 대하여 알게되었네요..우선 김형준님 감사합니다.
      저도 요즘 스마트폰 3D게임 개발을 대비해서 OpenGL ES 책 한권 구입하려는데, 김형준님이 번역한 OpenGL ES 서적 이름이 뭔지 알 수 있을까요?
      검색해보니 ‘모바일 3D 그래픽스’라는 책이 나오긴 하던데요…

  2. 안녕하십니까^^ 이렇게 OPENGL을 차근차근 개념적으로 설명 하셔서 너무 많은 도움이 된 듯 합니다.
    제가 3D 초보 개발자 인데 고수님의 의견을 듣고자 이렇게 글을 남겨 봅니다.
    제가 하는 것은 2D를 그려 그걸 딜로니 삼각화와 넙스 곡면 처리등을 통해 3D로 만들어 주는 것입니다. 또 한 텍스쳐링 까지 가능 하고요.

    하지만 여기서 문제점이 있어 여쭤 봅니다. 텍스쳐링을 하게 될 때 PNG파일로 택스쳐링을 하게 되면 원하는 결과가 나오지 않아 답답하기 짝이 없습니다.

    PNG의 투명한 부분이 검정색으로 보여져 버립니다. 제가 원레 원한 결과는 투명한 부분은 투명하여 원레의 형상이 보이고 그렇지 아니한 부분은 png 파일에서의 rgb 값을 보여지게 하고 싶습니다.
    블렌딩으로 하게 되면 완전 투시(?)가 되어 버려 큐브를 예로 들자면 큐브의 면까지도 날아가버립니다.

    저는 png 텍스쳐를 단지 큐브 위에 올린다는 생각으로 입히고 싶은데 좋은 말씀이나 구현 방법이 있으시면 쪼금 도움을 주시면 정말 감사하겠습니다. magicpd@nate.com 제 메일 주소입니다.^^

    ps. 정말 좋은 글들 잘 보고 갑니다.

    1. 요즘 일로 이래 저래 치여 댓글을 이제서야 달아 드립니다. 기억이 가물가물한데.. 텍스쳐링도 알파값에 의해 투명도 처리가 되는 것으로 알고 있습니다. http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=08 를 참고하시기 바랍니다. 일단 블랜딩과 밀접한 관련이 있으시 위의 url을 참고하시고 그래도 않된다면 다시 한번 더 글을 남겨주시기 바랍니다.

    2. 저도 몇일전에 비슷한 일로 애먹었었습니다.

      initial할때 다음과같이 설정해보세요.
      glColorMask (TRUE, TRUE, TRUE, TRUE)

  3. 감사합니다.도움이 많이 되었습니다. 기본 개념 잡기 힘들었는데 처음부터 차근차근 읽어 보니 머리속에 정리가 되네요. 복받으실꺼에요.

  4. 강좌 잘 보고 있고, 많은 도움이 되어 정말 감사합니다.
    제가 여기에 올라와 있는 multitexture에 대한 첫번째 샘플 프레그먼트 쉐이더를 사용해 보았습니다만.
    잘 적용이 안되는 것 같군요.
    프로그램 상에서는 아래와 같이 값을 어래와 같이 설정을 하였구요.

    ////////////////////////////////////////////
    GLint success;
    glValidateProgramARB(m_progObj[nWhichShader]);
    glGetObjectParameterivARB(m_progObj[nWhichShader], GL_OBJECT_VALIDATE_STATUS_ARB, &success);
    if (success)
    {
    GLint uniformLoc;
    glUseProgramObjectARB(m_progObj[nWhichShader]);
    uniformLoc = glGetUniformLocationARB(m_progObj[nWhichShader], “tex”);
    if (uniformLoc != -1) glUniform1iARB(uniformLoc, 0);
    GLint ShaderLoc;
    ShaderLoc = glGetUniformLocationARB(m_progObj[nWhichShader], “l3d”);
    if (ShaderLoc != -1) glUniform1iARB(ShaderLoc, 1);
    }
    ////////////////////////////////////////////

    그런데, texture 2개가 합성이 되지를 않는군요.
    첫번째 texture만 나오는군요.
    그래픽 카드는 Geforce를 사용하고 있구요.

    프로그램에서 “tex”, “l3d” 설정하는 방법이 잘못 된 것인지요?
    저의 예상으로는 이 부분때문에 문제인 것 같습니다만.

    좀 알려 주셨으면 정말 감사하겠습니다.
    아니면, 이런 것에 대한 프로그램 소스까지 같이 있는 자료나 책 추천 바랍니다.

  5. OpenGL 쉐이더는 저의 가장 큰 관심 영역 중 하나입니다.. 하지만 아직 문외안 정도랍니다. 질문하신 내용에 대한 명쾌한 답변을 드리지 못해 아쉽습니다…..

  6. 감사합니다!
    그런데 opengl에서 쉐이딩을 하려면 glsl만 가능한가요?
    hlsl로 작성된 것을 opengl에 적용하려면 glsl로 다시 작성해야하나요?ㅜㅜ

    1. hlsh(철자가 정확한가요?)은 DirectX의 쉐이딩 언어 아닌가요? 아마도 glsl과 언어차원에서 상호 변환해주는 툴이 있지 않을까… 생각만 해봅니다.

  7. 안녕하세요.. 답답한 마음에 또 댓글을 남깁니다.. OpenGL ES에서 구(파라미터 갯수에 따라 폴리곤의 갯수가 변경되는 형태)를 구현하긴 하였는데.. 텍스쳐가 제대로 입혀지지 않네요.. 구를 생성하는데 참고한 사이트는 http://efreedom.com/Question/1-1482351/Help-Drawing-Sphere-OpenGL-ES 이고
    구의 텍스쳐 좌표를 생성함에 있어 참고한 사이트는 http://cupper.egloos.com/1093468 입니다.. 함수는 아래와 같습니다..

    void OpenGL11::drawSphere(GLfloat fRadius, GLint iSlices, GLint iStacks){
    //GLfloat *vertexPointer = (GLfloat *)malloc(sizeof(GLfloat) * iStacks * (iSlices + 1) * 3 * 2);
    GLfloat *vertexPointer = (GLfloat *)Osp_alloc(sizeof(GLfloat) * iStacks * (iSlices + 1) * 3 * 2);
    GLfloat *texturePointer = (GLfloat *)Osp_alloc(sizeof(GLfloat) * iStacks * (iSlices + 1) * 2 * 2);

    GLfloat drho = (GLfloat)(3.141592653589) / (GLfloat) iStacks;
    GLfloat dtheta = 2.0f * (GLfloat)(3.141592653589) / (GLfloat) iSlices;
    GLfloat ds = 1.0f / (GLfloat) iSlices;
    GLfloat dt = 1.0f / (GLfloat) iStacks;
    GLfloat t = 1.0f;
    GLfloat s = 0.0f;
    GLint i, j; // Looping variables

    int idx = 0;
    for (i = 0; i < iStacks; i++) { GLfloat rho = (GLfloat)i * drho; GLfloat srho = (GLfloat)(sin(rho)); GLfloat crho = (GLfloat)(cos(rho)); GLfloat srhodrho = (GLfloat)(sin(rho + drho)); GLfloat crhodrho = (GLfloat)(cos(rho + drho)); s = 0.0f; for ( j = 0; j <= iSlices; j++) { GLfloat theta = (j == iSlices) ? 0.0f : j * dtheta; GLfloat stheta = (GLfloat)(-sin(theta)); GLfloat ctheta = (GLfloat)(cos(theta)); GLfloat x = stheta * srho; GLfloat y = ctheta * srho; GLfloat z = crho; glNormal3f(x, y, z); vertexPointer[idx + 0] = x * fRadius; vertexPointer[idx + 1] = y * fRadius; vertexPointer[idx + 2] = z * fRadius; texturePointer[idx + 1] = (GLfloat)(Math::Acos(vertexPointer[idx + 0] / fRadius) / M_PI); texturePointer[idx + 0] = (GLfloat)((Math::Acos(vertexPointer[idx + 2] / (fRadius * (sin(texturePointer[idx + 1] * M_PI)))) / (2 * M_PI))); x = stheta * srhodrho; y = ctheta * srhodrho; z = crhodrho; s += ds; glNormal3f(x, y, z); vertexPointer[idx + 3] = x * fRadius; vertexPointer[idx + 4] = y * fRadius; vertexPointer[idx + 5] = z * fRadius; texturePointer[idx + 2] = (GLfloat)(Math::Acos(vertexPointer[idx + 3] / fRadius) / M_PI); texturePointer[idx + 3] = (GLfloat)((Math::Acos(vertexPointer[idx + 5] / (fRadius * (sin(texturePointer[idx + 2] * M_PI)))) / (2 * M_PI))); idx += 6; } t -= dt; } glVertexPointer(3, GL_FLOAT, 0, vertexPointer); glEnableClientState(GL_VERTEX_ARRAY); glEnable(GL_TEXTURE_2D); glTexCoordPointer(2, GL_SHORT, 0, texturePointer); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindTexture(GL_TEXTURE_2D, 12); glDrawArrays(GL_TRIANGLE_STRIP, 0, iStacks * iSlices * 2 ); delete vertexPointer; delete texturePointer; } 이틀 밤을 새가면서 이것저것 참고해서 수정하는 중인데 뭐가 문제인지 실마리조차 잡히지 않네요.. 간단한 힌트라도 부탁드리겠습니다..

  8. 안녕하세요 ^^ 간만입니다.. 지난번에 고민하던 사항은 해결이 되었습니다.. 또 하나 문제점이 생겼는데요.. ㅎ OpenGL ES로 M2D 로더를 구현중에 있는데, 정상적으로 불러오기는 하는듯 한데.. 텍스쳐 매핑이 문제네요.. 씌워지기는 하나 좀 이상하게 씌워지는데, 인터넷에 있는 대부분의 코드들이 ES용이 아니라서 포팅을 하는데 난항이 있습니다.. 텍스쳐 좌표를 약간 수정만 한다면 해결이 될것 같은데 원리를 잘 모르는 상태라 쉽지가 않네요 텍스쳐 좌표쪽 힌트좀 부탁드리겠습니다..
    (주석이 되어 있는 부분이 GL용 원본코드이고 다른 부분이 ES용으로 포팅한 제 코드부분입니다)
    void OpenGL11::DisplayMD2TextureNumber(modelData_t *model, int frameNum, int textureNumber)
    {
    vector_t *pointList;
    int i;

    // create a pointer to the frame we want to show
    pointList = &model->pointList[model->numPoints * frameNum];

    // set the texture
    glBindTexture(GL_TEXTURE_2D, 16);//model->modelTex->texID);

    // display the textured model with proper lighting normals
    // glBegin(GL_TRIANGLES);

    GLfloat * myVertexPointer = (GLfloat *)Osp_alloc(sizeof(GLfloat) * (model->numTriangles + 1) * 9);
    GLfloat * myTexturePointer = (GLfloat *)Osp_alloc(sizeof(GLfloat) * (model->numTriangles + 1) * 6);

    int index = 0;
    int index2 = 0;

    for(i = 0; i < model->numTriangles; i++)
    {
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    myVertexPointer[index + 0] = pointList[model->triIndex[i].meshIndex[0]].point[0];
    myVertexPointer[index + 1] = pointList[model->triIndex[i].meshIndex[0]].point[1];
    myVertexPointer[index + 2] = pointList[model->triIndex[i].meshIndex[0]].point[2];
    myVertexPointer[index + 3] = pointList[model->triIndex[i].meshIndex[1]].point[0];
    myVertexPointer[index + 4] = pointList[model->triIndex[i].meshIndex[1]].point[1];
    myVertexPointer[index + 5] = pointList[model->triIndex[i].meshIndex[1]].point[2];
    myVertexPointer[index + 6] = pointList[model->triIndex[i].meshIndex[2]].point[0];
    myVertexPointer[index + 7] = pointList[model->triIndex[i].meshIndex[2]].point[1];
    myVertexPointer[index + 8] = pointList[model->triIndex[i].meshIndex[2]].point[2];

    CalculateNormal(pointList[model->triIndex[i].meshIndex[0]].point, pointList[model->triIndex[i].meshIndex[2]].point, pointList[model->triIndex[i].meshIndex[1]].point);

    myTexturePointer[index2 + 0] = (GLfloat)model->st[model->triIndex[i].stIndex[0]].s;
    myTexturePointer[index2 + 1] = (GLfloat)model->st[model->triIndex[i].stIndex[0]].t;
    myTexturePointer[index2 + 2] = (GLfloat)model->st[model->triIndex[i].stIndex[1]].s;
    myTexturePointer[index2 + 3] = (GLfloat)model->st[model->triIndex[i].stIndex[1]].t;
    myTexturePointer[index2 + 4] = (GLfloat)model->st[model->triIndex[i].stIndex[2]].s;
    myTexturePointer[index2 + 5] = (GLfloat)model->st[model->triIndex[i].stIndex[2]].t;

    index += 9;
    index2 += 6;

    /*
    glTexCoord2f(model->st[model->triIndex[i].stIndex[0]].s, model->st[model->triIndex[i].stIndex[0]].t);
    glVertex3fv(pointList[model->triIndex[i].meshIndex[0]].point);

    glTexCoord2f(model->st[model->triIndex[i].stIndex[2]].s, model->st[model->triIndex[i].stIndex[2]].t);
    glVertex3fv(pointList[model->triIndex[i].meshIndex[2]].point);

    glTexCoord2f(model->st[model->triIndex[i].stIndex[1]].s, model->st[model->triIndex[i].stIndex[1]].t);
    glVertex3fv(pointList[model->triIndex[i].meshIndex[1]].point);
    */
    }

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glTexCoordPointer(2, GL_FLOAT, 0, myTexturePointer);
    glVertexPointer(3, GL_FLOAT, 0, myVertexPointer);
    glDrawArrays(GL_TRIANGLES, 0, model->numTriangles * 3);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    delete(myVertexPointer);
    delete(myTexturePointer);
    // glEnd();
    }

  9. 안녕하세요 김형준님.
    덕분에 OpenGL 기초를 다지는데 많이 도움이 되고 있습니다.

    저희가 OpenCV와 OpenGL을 이용해서 증강현실 프로젝트를 하고있는데요
    OpenGL로 3dmax로 만든 파일을 불러와서 컨트롤 하려그러는데,
    3d객체만 불러오는 것까지는 잘되는데 애니메이션까지 불러오는 것에서
    헤메고 있어서 프로젝트 진행에 어려움을 겪고 있습니다.
    3ds에서 객체와 애니메이션을 불오는 예제나 강좌가 찾기 어렵더라구요.

    그래서 혹시 조언을 해주실 수 있나 해서 글 남겨봅니다.

  10. 죄송합니다. 현재 다른 분들의 질문에 대해서도 답변을 못드리고 있네요….
    쉽게 답변 드리기 어렵기도 하거니와.. 제 스스로도 여유가 없답니다…

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다