`
897371388
  • 浏览: 535261 次
文章分类
社区版块
存档分类
最新评论

OpenGL进阶(十六) - GLSL纹理(Texture)

 
阅读更多

提要

纹理是实时渲染中的非常基础且非常重要的一个主题,它一直作为增强渲染效果的一个强有力手段。在固定渲染管线的opengl中,纹理的使用灵活度非常有限,有了shader之后,我们可以在shader中操作纹理,这时就可以用一些额外的渲染参数来渲染纹理,比如位移图(displacement maps),法向量(normal vectors)等等。

实际上在OpenGL4.0中,纹理不仅仅是图像信息,更准确的,它应该是内存中一个块区。


UV贴图

这是最简单的一种贴图了。从文件读取图片文件,然后绑定贴图,绘制到顶点。

一般的3D模型制作软件都可以制作UV贴图,推荐使用Blender + GIMP,制作的方法可以参考这里


接着按照前面说过的方法导出box.obj文件,待会用来加载。

在Blender中导出obj文件中,没有设置和设置材质导出的最终格式并不一样,不止是有无纹理坐标的问题,面定义的格式也改变了,这里需要重写一个加载obj文件的函数。

bool Util::loadObjWithTex(const char * path,std::vector<glm::vec3> & out_vertices,std::vector<glm::vec2> & out_uvs,std::vector<glm::vec3> & out_normals)
{
    printf("Loading OBJ file with tex %s...\n", path);
    std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
    std::vector<glm::vec3> temp_vertices;
    std::vector<glm::vec2> temp_uvs;
    std::vector<glm::vec3> temp_normals;

    FILE * file = fopen(path, "r");
    if( file == NULL ){
        printf("Impossible to open the file ! Are you in the right path ? \n");
        return false;
    }
    while( 1 ){
        char lineHeader[128];
        // read the first word of the line
        int res = fscanf(file, "%s", lineHeader);
        if (res == EOF) break; // EOF = End Of File. Quit the loop.
        // else : parse lineHeader
        if ( strcmp( lineHeader, "v" ) == 0 ){
            //cout<<"Get v"<<endl;
            glm::vec3 vertex;
            fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z );
            temp_vertices.push_back(vertex);
        }else if ( strcmp( lineHeader, "vt" ) == 0 ){
            //cout<<"Get vt"<<endl;
            glm::vec2 uv;
            fscanf(file, "%f %f\n", &uv.x, &uv.y );
            //cout<<"uv.x"<<uv.x<<" uv.y"<<uv.y;
            uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
            temp_uvs.push_back(uv);
        }else if ( strcmp( lineHeader, "vn" ) == 0 ){
           // cout<<"Get vn"<<endl;
            glm::vec3 normal;
            fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z );
            temp_normals.push_back(normal);
        }else if ( strcmp( lineHeader, "f" ) == 0 ){
            //cout<<"Get f"<<endl;
            std::string vertex1, vertex2, vertex3;
            unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
            int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2] );
            if (matches != 9){
                printf("File can't be read by our simple parser :-( Try exporting with other options\n");
                return false;
            }
            vertexIndices.push_back(vertexIndex[0]);
            vertexIndices.push_back(vertexIndex[1]);
            vertexIndices.push_back(vertexIndex[2]);
            uvIndices.push_back(uvIndex[0]);
            uvIndices.push_back(uvIndex[1]);
            uvIndices.push_back(uvIndex[2]);
            normalIndices.push_back(normalIndex[0]);
            normalIndices.push_back(normalIndex[1]);
            normalIndices.push_back(normalIndex[2]);
        }else{
            // Probably a comment, eat up the rest of the line
            char stupidBuffer[1000];
            fgets(stupidBuffer, 1000, file);
        }
    }

// For each vertex of each triangle
    for( unsigned int i=0; i<vertexIndices.size(); i++ ){
        // Get the indices of its attributes
        unsigned int vertexIndex = vertexIndices[i];
        unsigned int uvIndex = uvIndices[i];
        unsigned int normalIndex = normalIndices[i];
        // Get the attributes thanks to the index
        glm::vec3 vertex = temp_vertices[ vertexIndex-1 ];
        glm::vec2 uv = temp_uvs[ uvIndex-1 ];
        glm::vec3 normal = temp_normals[ normalIndex-1 ];
        // Put the attributes in buffers
        out_vertices.push_back(vertex);
        out_uvs     .push_back(uv);
        out_normals .push_back(normal);
        }
    return true;
}

注意在加载uv坐标的时候y轴需要转换一下。

在initGL中需要启动2D纹理,添加一句:

glEnable(GL_TEXTURE_2D);

同时还要添加的是纹理的加载函数,用到了SDL的image库。

首先是在codeblocks工程中把库添加进来:



然后在CGL类中添加方法:

bool CGL::loadTexture(const char *name)
{
    int mode;
    GLuint texture;
    SDL_Surface* surface = IMG_Load(name);
    SDL_SetAlpha(surface, SDL_SRCALPHA, 0);
    if (surface==NULL) { //If it failed, say why and don't continue loading the texture
        cout<<"Error:" <<SDL_GetError()<<endl;
        return 0;
    }

    // work out what format to tell glTexImage2D to use...
    if (surface->format->BytesPerPixel == 3) { // RGB 24bit
            mode = GL_RGB;
        } else if (surface->format->BytesPerPixel == 4) { // RGBA 32bit
            mode = GL_RGBA;
        } else {
            SDL_FreeSurface(surface);
            return 0;
        }
    glActiveTexture(GL_TEXTURE0);
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, mode, surface->w,surface->h, 0, mode,GL_UNSIGNED_BYTE,surface->pixels);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT );
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT );

    if(surface) SDL_FreeSurface(surface);

    prog.setUniform("Tex1", 0);
    return true;
}

接下来是最重要的shader了,首先看顶点shader。

#version 400
layout (location = 0) in vec3 VertexPosition;  
layout (location = 1) in vec3 VertexNormal;  
layout (location = 2) in vec2 VertexTexCoord;

out vec4 Position;
out vec3 Normal;
out vec2 TexCoord;

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;


void getEyeSpace(out vec3 norm, out vec4 position)
{
	norm =  normalize(NormalMatrix * VertexNormal);
	position = ModelViewMatrix * vec4(VertexPosition, 1.0);
}


void main()
{
	getEyeSpace(Normal, Position);
	TexCoord = VertexTexCoord;
	gl_Position = MVP * vec4( VertexPosition, 1.0);
}

VertexPosition,VertexNormal.VertexTexCoord 都是作为输入的属性传进来的。

这里的layout用于一个具体变量前,用于显式标明该变量的一些布局属性,location的值和前面glBindAttribLocation的位置是一一对应的。

在顶点shader中获取VertexTexCoord之后,不经处理直接扔给Fregment shader,接下来在片段 shader中:

#version 400

in vec4 Position;
in vec3 Normal;
in vec2 TexCoord;

struct LightInfo{
	vec4 position;
	vec3 intensity;
};

struct MaterialInfo{
	vec3 Ka;
	vec3 Kd;
	vec3 Ks;
	float Shininess;
};

uniform sampler2D Tex1; 
uniform LightInfo Light;
uniform	MaterialInfo Material;

void phongModel(vec4 position, vec3 norm, out vec3 amb, out vec3 diff, out vec3 spec)
{
	vec3 tmp = vec3(0.9f, 0.5f, 0.3f);
	vec3 s = normalize(vec3(Light.position - position));
	vec3 v = normalize(-position.xyz);
	vec3 r = reflect(-s, norm);

	amb = Light.intensity * Material.Ka;
	float sDotN = max(dot(s, norm),0.0);
	diff = Light.intensity * Material.Kd * sDotN;
	spec = vec3(0.0);
	if(sDotN > 0.0)
		spec = Light.intensity *Material.Ks * pow( max( dot(r,v), 0.0 ),Material.Shininess );  
}

void main(void)
{
	vec3 amb,diff,spec;
	vec4 texColor = texture(Tex1, TexCoord);
	phongModel(Position, Normal, amb, diff, spec);
	
	//Render without light effect.
	gl_FragColor = (vec4( amb + diff, 1.0) * texColor) + vec4(spec, 1.0);

}


在main函数中主要是通过GLSL内置的纹理函数 - texture 来将与纹理坐标对应的纹理值从内存中取出来,接下来和光照的颜色一起混合,得到最后的颜色。

编译运行一下:




多纹理

多纹理的实现比较简单,就是将多个纹理加载到内存,然后混合得到最终纹理的颜色。

纹理的加载函数需要修改一下:

bool CGL::loadTexture()
{
    int mode;
    GLuint texIDs[2];
    glGenTextures(2, texIDs);
    // Load brick brake file
    const char * texName = "assets/textures/crate.bmp";
    SDL_Surface* surface = IMG_Load(texName);
    SDL_SetAlpha(surface, SDL_SRCALPHA, 0);
    if (surface==NULL) { //If it failed, say why and don't continue loading the texture
        cout<<"Error:" <<SDL_GetError()<<endl;
        return 0;
    }

    // work out what format to tell glTexImage2D to use...
    if (surface->format->BytesPerPixel == 3) { // RGB 24bit
        mode = GL_RGB;
    } else if (surface->format->BytesPerPixel == 4) { // RGBA 32bit
        mode = GL_RGBA;
    } else {
        SDL_FreeSurface(surface);
        return 0;
    }

    // Copy file to OpenGL
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texIDs[0]);
    glTexImage2D(GL_TEXTURE_2D, 0, mode, surface->w,surface->h, 0, mode,GL_UNSIGNED_BYTE,surface->pixels);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    // Load moss texture file
    texName = "assets/textures/smile.png";
    surface = IMG_Load(texName);
    SDL_SetAlpha(surface, SDL_SRCALPHA, 0);
    if (surface==NULL) { //If it failed, say why and don't continue loading the texture
        cout<<"Error:" <<SDL_GetError()<<endl;
        return 0;
    }

    // work out what format to tell glTexImage2D to use...
    if (surface->format->BytesPerPixel == 3) { // RGB 24bit
        mode = GL_RGB;
    } else if (surface->format->BytesPerPixel == 4) { // RGBA 32bit
        mode = GL_RGBA;
    } else {
        SDL_FreeSurface(surface);
        return 0;
    }

    // Copy file to OpenGL
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texIDs[1]);
    glTexImage2D(GL_TEXTURE_2D, 0, mode, surface->w,surface->h, 0, mode,GL_UNSIGNED_BYTE,surface->pixels);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    if(surface) SDL_FreeSurface(surface);

    prog.setUniform("Tex1", 0);
    prog.setUniform("Tex2", 1);
    return true;
}

如果是加载更多的纹理的话,可以使用纹理Buffer,那样可以获得更高的效率,这里就偷懒了 - -。

然后修改fregment shader:

#version 400

in vec4 Position;
in vec3 Normal;
in vec2 TexCoord;

struct LightInfo{
	vec4 position;
	vec3 intensity;
};

struct MaterialInfo{
	vec3 Ka;
	vec3 Kd;
	vec3 Ks;
	float Shininess;
};

uniform sampler2D Tex1; 
uniform sampler2D Tex2; 

uniform LightInfo Light;
uniform	MaterialInfo Material;

void phongModel(vec4 position, vec3 norm, out vec3 amb, out vec3 diff, out vec3 spec)
{
	vec3 tmp = vec3(0.9f, 0.5f, 0.3f);
	vec3 s = normalize(vec3(Light.position - position));
	vec3 v = normalize(-position.xyz);
	vec3 r = reflect(-s, norm);

	amb = Light.intensity * Material.Ka;
	float sDotN = max(dot(s, norm),0.0);
	diff = Light.intensity * Material.Kd * sDotN;
	spec = vec3(0.0);
	if(sDotN > 0.0)
		spec = Light.intensity *Material.Ks * pow( max( dot(r,v), 0.0 ),Material.Shininess );  
}

void main(void)
{
	vec3 amb,diff,spec;
	vec4 crateColor = texture(Tex1, TexCoord);
	vec4 mosColor = texture(Tex2, TexCoord);
	phongModel(Position, Normal, amb, diff, spec);
	
	vec4 texColor = mix(crateColor,mosColor,mosColor.a);
	//Render without light effect.
	gl_FragColor = (vec4( amb + diff, 1.0) * texColor) + vec4(spec, 1.0);

}

最终的纹理颜色是用内建的mix函数混合得到的。运行效果:



源码下载

参考

OpenGL 4.0 Shading Langage Cookbook


分享到:
评论

相关推荐

    SDL2-OpenGL:这是有关使用SDL2作为OpenGL Helper的GLSL着色器的入门系列,以及有关C-Coders的一些着色器语言。 最后使用Shadertoy着色器

    0-初始化一个简单的OpenGL窗口1-绘制和旋转矩形texture_SOIL texture_SDL2-渲染纹理2-使用着色器3-加载着色器3a1-按n创建新的着色器,将在编辑器中打开如果文件已更改并保存。 按c进行编译。 3a2-应该跟踪编辑器中...

    Qt Openglwidget 显示图片纹理贴图

    Qt5.7+VS2015 64位的环境,使用Qt的QOpenglWidget,来显示一张图片,采用GLSL。学习入门挺适合的。在网上找了很久资源,还是自己折腾搞定,特地分享出来。

    win opengl qt draw texture mixed picture

    在windows 下,使用qt opengl 绘制纹理,并将两张图片采样色彩混合成一张图片。

    rbGLox:适用于快速原型制作的 ruby​​-opengl 和 ruby​​-glfw 包装器

    格洛克斯rbGLox 是 ruby​​-opengl、ruby-glfw 系列的一个小包装,通过抽象和提供一些高级接口来处理“纹理”、“GLSL 着色器”、基本“几何”等。 它是快速原型制作新的和有趣的想法的理想选择。 rbGLox是的姊妹...

    3D图像 纹理算法 类似openGL的一个实现

    3D图形处理中的算法,类似openGL的一个实现 进一步可以把浮点改成定点增加速度

    OpenGLEngine:基于OpenGL实现的渲染引擎

    ios工程引用的资源全部是PVRTC压缩格式的纹理,需要手动转换,运行下面脚本即可生成对应的纹理 cd tools sh texture.sh sh ios_hash.sh 注意生成时间会比较长,耐心等待 版 qt 5.11 opengl 330 opengles 300 ...

    WebGL编程指南

    在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 ...

    webgl编程指南及源码1/2

    在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 ...

    webgl编程指南及源码2/2

    在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 ...

    glslViewer:用于2D3D着色器的基于控制台的GLSL沙箱

    glslViewer GlslViewer是一个基于控制台的灵活OpenGL沙盒,无需用户界面即可显示2D / 3D GLSL着色器。 您绝对可以使用Python模块(包括)或任何其他与glslViewer来回通信的工具(使用标准POSIX控制台输入/输出或OSC...

    Imogen:GPU纹理生成器

    可在此处找到Web版本: : 当前,节点可以用GLSL或C或Python编写。 使用CMake和VisualStudio进行构建。 Windows和Web构建可用。 Web Edition的局限性: 没有线程作业 没有C / Python节点 没有Python插件 没有文件...

    WebGL编程指南压缩包

    在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 ...

    OpenSceneGraph三维渲染引擎设计与实践

    7.2.3 渲染到纹理(render to texture) 181 7.2.4 范例:将场景渲染到纹理 183 7.3 视景器 186 7.3.1 视景器的主要工作 186 7.3.2 单视景器与多视景器 188 7.3.3 范例:投影墙显示 191 7.3.4 范例:多视景器...

Global site tag (gtag.js) - Google Analytics