본문 바로가기
전공/컴퓨터 그래픽스

텍스처 (3)3D 텍스쳐

by 임 낭 만 2023. 5. 28.

GLEW 설치

  • GLEW(The OpenGL Extension Wrangler Library)는 여러 종류의 컴퓨터 플랫폼(Cross Platform) 환경에서 작동 가능한 오픈소스 형태의 C/C++ 확장 라이브러리임
  • GLEW는 효율적인 실시간(run time) 메커니즘을 제공하는데, 이 메커니즘은 사용자의 플랫폼(Window, Mac 등…)에 적합한 OpenGL 기능들을 결정함
  • GLEW는 다양한 운영체제를 지원 (GLEW에서지원하는 OS: Windows, Linux, Mac OS X, FreeBSD, Irix, Solaris)

3D Textures

3D Textures

  • 3D textures are raster graphics with three dimensions
  • Such volumetric data is often generated in medical imaging
    • A CT or MRI machine takes several cuts through the investigated object
    • Each slice represents a 2D raster graphics and can be assembled to a 3D volume by stacking the slice
  • The texture coordinates $s$ and $t$, as known from 2D textures, are extended by a new dimension $r$, which is orthogonal to the two existing ones

 

더보기

3D 텍스처는 3차원의 래스터 그래픽

이러한 볼륨 데이터는 의료 영상에서 종종 생성됨

- CT 또는 MRI 기계가 조사 대상물을 여러 번 잘라냄.

- 각 슬라이스는 2D 래스터 그래픽을 나타내며 슬라이스를 적층하여 3D 볼륨으로 조립할 수 있음

2D 텍스처에서 알려진 텍스처 좌표 s와 t는 기존의 두 텍스처에 직교하는 새로운 치수 r에 의해 확장됨

3D Textures in OpenGL

  • In OpenGL the command for creation and application of 3D textures are consistent with the commands for 2D texture
  • The function glTexImage2D becomes glTexImages3D and GL_TEXTURE_2D becomes GL_TEXTURE_3D, etc.
  • When passing texture data to glTeImage3D the memory layout can be interpreted as a series of 2D textures data
  • That is, when filling the memory, the texel in $s$-direction is increased in each step, in $t$-direction after width texel values, and in $r$-direction only after width*height texel values
더보기

OpenGL에서는 3D 텍스처의 작성 및 적용을 위한 명령어는 2D 텍스처를 위한 명령어와 일치함

glTexImage2D 함수는 glTexImages3D가 되고 GL_TEXIture_2D는 GL_TEXIture_3D 등이 됨

텍스처 데이터를 glTeImage3D에 전달하면 메모리 레이아웃은 일련의 2D 텍스처 데이터로 해석할 수 있음.

즉, 메모리를 채울 때 s-방향의 텍스트는 각 단계에서 증가하고, 너비 텍스트 값 이후의 t-방향, 너비*높이 텍스트 값 이후의 r-방향으로만 증가함

Example: Texturing a Random Terrain

// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager

#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
using namespace std;
class Renderer {
public:
    float t;
private:
    GLuint texID;
    std::vector <float> terrain;
public:
    // constructor
    Renderer() : t(0.0), texID(0) {}
    //destructor
    ~Renderer() {
        if (texID != 0) glDeleteTextures(1, &texID);
    }
public:
    void init() {
        glEnable(GL_DEPTH_TEST);
        std::vector< std::string > filenames;
        filenames.push_back("deep_water.ppm");
        filenames.push_back("shallow_water.ppm");
        filenames.push_back("shore.ppm");
        filenames.push_back("fields.ppm");
        filenames.push_back("rocks.ppm");
        filenames.push_back("snow.ppm");
        texID = loadTexture3D(filenames);

        rebuildTerrain();
    }
    void resize(int w, int h) {	//윈도우 사이즈
        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(30.0, (float)w / (float)h, 0.1, 50.0);
    }
    void display() {	//뷰포트
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        // set camera
        gluLookAt(1.5, -1.0, 1.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
        // draw scene
        glRotatef(t, 0.0f, 0.0f, 1.0f);
        drawTerrain();
    }
    void rebuildTerrain() {	//1번 누르면
        //create random values
        int dim = 40;
        terrain.resize(dim * dim);
        for (int r = 0; r < dim * dim; r++) {
            int rval = rand();
            terrain[r] = (fabs(float(rval)) / float(RAND_MAX));
        }
        if (true) { // generate smooth terrain values
            std::vector<float> smoothTerrain(dim * dim);
            for (unsigned k = 0; k < 5; k++) {
                float maxVal = 0.0f;
                float minVal = 1.0f;
                for (int x = 0; x < dim; x++) {
                    for (int y = 0; y < dim; y++) {
                        if (x == 0 || x == dim - 1) terrain[x * dim + y] = 0.0f;
                        else if (y == 0 || y == dim - 1) terrain[x * dim + y] = 0.0f;
                        else {
                            float a = 0.0f;
                            int counter = 0;
                            for (int s = -1; s <= 1; s++) {
                                for (int r = -1; r <= 1; r++) {
                                    a += terrain[(x + s) * dim + (y + r)];
                                    counter++;
                                }
                            }
                            float val = a / float(counter);
                            smoothTerrain[x * dim + y] = val;
                            if (val > maxVal) maxVal = val;
                            if (val < minVal) minVal = val;
                        }
                    }
                }
                for (int r = 0; r < dim * dim; r++) terrain[r] = (smoothTerrain[r] - minVal) / (maxVal - minVal);
            }
        }
    }
private:
    // returns a valid textureID on success, otherwise 0
    GLuint loadTexture3D(std::vector< std::string >& filenames) {
        unsigned width = 0;
        unsigned height = 0;
        unsigned depth = unsigned(filenames.size());
        int level = 0;
        int border = 0;
        std::vector<unsigned char> imgData;
        std::vector<unsigned char> data3d;
        unsigned prevWidth = 0;
        unsigned prevHeight = 0;
        for (unsigned i = 0; i < depth; i++) {
            // load image data
            if (!loadPPMImageFlipped(filenames[i], width, height, imgData)) return 0;
            if (i != 0 && (prevWidth != width || prevHeight != height)) return 0;

            // pack 2D images subsequently into a large 3D buffer
            data3d.insert(data3d.end(), imgData.begin(), imgData.end());
            prevWidth = width;
            prevHeight = height;
        }
        // data is aligned in byte order
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        //request textureID
        GLuint textureID;
        glGenTextures(1, &textureID);
        // bind texture
        glBindTexture(GL_TEXTURE_3D, textureID);
        //parameters the define how to warp the texture
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
        GLfloat borderColor[4] = { 0.0f,0.0f,0.0f,1.0f };
        glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor);
        //define how to filter the texture
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        //texture colors should replace the original color values
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE
        // specify the 2D texture map
        glTexImage3D(GL_TEXTURE_3D, level, GL_RGB, width, height, depth, border, GL_RGB, GL_UNSIGNED_BYTE, &data3d[0]);
        // return unique texture identifier
        return textureID;
    }
    void drawTerrain() {
        glEnable(GL_TEXTURE_3D);

        glBindTexture(GL_TEXTURE_3D, texID);
        glColor3f(1.0f, 0.0f, 0.0f);
        unsigned dim = unsigned(sqrt(double(terrain.size())));
        float maxHeight = 0.2f;
        float texHeight = 0.9f;
        for (unsigned x = 1; x < dim; x++) {
            for (unsigned y = 1; y < dim; y++) {
                glBegin(GL_POLYGON);
                glTexCoord3f(float(x - 1) / float(dim),
                    float(y - 1) / float(dim),
                    terrain[(x - 1) * dim + (y - 1)] * texHeight);
                glVertex3f(float(x - 1) / float(dim) - 0.5f,
                    float(y - 1) / float(dim) - 0.5f,
                    terrain[(x - 1) * dim + (y - 1)] * maxHeight);
                glTexCoord3f(float(x) / float(dim),
                    float(y - 1) / float(dim),
                    terrain[x * dim + (y - 1)] * texHeight);
                glVertex3f(float(x) / float(dim) - 0.5f,
                    float(y - 1) / float(dim) - 0.5f,
                    terrain[x * dim + (y - 1)] * maxHeight);
                glTexCoord3f(float(x) / float(dim),
                    float(y) / float(dim),
                    terrain[x * dim + y] * texHeight);
                glVertex3f(float(x) / float(dim) - 0.5f,
                    float(y) / float(dim) - 0.5f,
                    terrain[x * dim + y] * maxHeight);
                glTexCoord3f(float(x - 1) / float(dim),
                    float(y) / float(dim),
                    terrain[(x - 1) * dim + y] * texHeight);
                glVertex3f(float(x - 1) / float(dim) - 0.5f,
                    float(y) / float(dim) - 0.5f,
                    terrain[(x - 1) * dim + y] * maxHeight);
                glEnd();
            }
        }
        glDisable(GL_TEXTURE_3D);
    }
    bool loadPPMImageFlipped(std::string& filename, unsigned& width, unsigned& height, std::vector<unsigned char>& imgData) {
        ifstream input(filename.c_str(), ifstream::in | ifstream::binary);
        if (!input) { // cast istream to bool to see if something went wrong
            cerr << "Can not find texture data file " << filename.c_str() << endl;
            return false;
        }
        input.unsetf(std::ios_base::skipws);

        string line;
        input >> line >> std::ws;
        if (line != "P6") {
            cerr << "File is not PPM P6 raw format" << endl;
            return false;
        }

        width = 0;
        height = 0;
        unsigned depth = 0;
        unsigned readItems = 0;
        unsigned char lastCharBeforeBinary;

        while (readItems < 3) {
            input >> std::ws;
            if (input.peek() != '#') {
                if (readItems == 0) input >> width;
                if (readItems == 1) input >> height;
                if (readItems == 2) input >> depth >> lastCharBeforeBinary;
                readItems++;
            }
            else { // skip comments
                std::getline(input, line);
            }
        }
        if (depth >= 256) {
            cerr << "Only 8-bit PPM format is supported" << endl;
            return false;
        }
        unsigned byteCount = width * height * 3;
        imgData.resize(byteCount);
        input.read((char*)&imgData[0], byteCount * sizeof(unsigned char));
        // vertically flip the image because the image origin
        // in OpenGL is the lower-left corner
        unsigned char tmpData;
        for (unsigned y = 0; y < height / 2; y++) {
            int sourceIndex = y * width * 3;
            int targetIndex = (height - 1 - y) * width * 3;
            for (unsigned x = 0; x < width * 3; x++) {
                tmpData = imgData[targetIndex];
                imgData[targetIndex] = imgData[sourceIndex];
                imgData[sourceIndex] = tmpData;
                sourceIndex++;
                targetIndex++;
            }
        }
        return true;
    }
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
    renderer->resize(w, h);
}
static void glutDisplay()
{
    renderer->display();
    glutSwapBuffers();
    glutReportErrors();
}
static void timer(int v)
{
    float offset = 0.25f;
    renderer->t += offset;
    glutDisplay();
    glutTimerFunc(unsigned(20), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
    bool redraw = false;
    switch (key) {
    case '1':
        renderer->rebuildTerrain();
        redraw = true;
        break;
    }
    if (redraw) {
        glutDisplay();
    }
}
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(320, 320);

    glutCreateWindow("Press 1 to generate a new random terrain");
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
    }
    glutDisplayFunc(glutDisplay);
    //glutIdleFunc(glutDisplay);
    glutReshapeFunc(glutResize);
    glutKeyboardFunc(glutKeyboard);

    renderer = new Renderer;
    renderer->init();

    glutTimerFunc(unsigned(20), timer, 0);

    glutMainLoop();
}

Texture Units

  • A graphics card has multiple texture units
  • This allows simultaneous access to multiple textures since each unit can provide a different texture
  • This becomes especially interesting when we start writing our own shaders
  • To support OpenGL 3.x, a graphics card must have at least 16 texture units
  • The maximum number f texture units can be queried by the command:
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &noOfTextureUnits);
  • The command glActiveTexture sets the currently modified texture unit:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texA);
//...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXUTRE_2D, texB);
//...
더보기

 

그래픽 카드에는 여러 텍스처 유닛이 있음.

이를 통해 유닛마다 다른 텍스처를 제공할 수 있기 때문에 여러 텍스처에 동시에 액세스할 수 있음.

이것은 독자적인 셰이더를 쓰기 시작할 때 특히 흥미로워짐.

OpenGL 3.x를 지원하려면 그래픽 카드에 최소 16개의 텍스처 유닛이 있어야 함.

OpenGL Texture Mapping from an Object-Oriented View

  • Texture ID (handle on texture object)
  • Properties of a texture object
    • Dimension (1D, 2D, 3D) and resolution (128x128, 256x256, …)
    • Component Type (ubyte, ushort, uint, float16, float32)
    • Number and semantic of the components (RGB, RGBA, BGR, …)
    • Data block for raster graphics and mipmap
    • Parameters for filtering and wrapping
  • Properties of the texture unit
    • Currently bounded texture object
    • Texture environment color
    • Parameters for the effect of the texture on the current fragment
    • Texture access function (disabled, 1D, 2D, 3D)
    • Texture transformation stack glMatrixMode(GL_TEXTURE)

댓글