s6.GLSL学习之纹理映射(三角形)

纹理

  通常来讲,计算机图像学的目标是决定组成图像的每个 部分的颜色。用高级着色算法计算像素的颜色是可能的,但这样的着色器的复杂度是很大的,以至于实现这样的方法是不实际的。因此,可以依赖纹理——大块的图像数据来绘制物体的表面使它们看起来更逼真。
  这里面包含很多内容,比如OpenGL中的高级纹理类型,包括数组纹理、立方体映射纹理、深度纹理和缓存纹理。这里先只介绍如何在应用程序中使用纹理映射,并通过一个示例来形象说明。
  一般纹理映射的步骤如下:
1. 创建一个纹理对象,为它加载纹素数据 。
2. 为顶点增加纹理坐标 。
3. 把纹理图与着色器中将要使用的纹理采样器关联。
4. 在着色器中使用纹理采样器来查询纹素值。

示例演示

  我们利用上面的纹理映射步骤来给一个三角形贴纹理。首先创建一个纹理对象,代码如下:

void MyQGLWidget::loadGLTextures()
{
    QImage tex, buf;
    if(!buf.load(":/new/img/texture.jpg")){
        QImage dummy(128, 128, QImage::Format_RGB32);
        dummy.fill(Qt::green);
        buf = dummy;
    }
    tex = QGLWidget::convertToGLFormat(buf);
    glGenTextures(1, &texture[0]);
    glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glBindTexture(GL_TEXTURE_2D, texture[0]);
}

  为了能够把纹理映射到三角形上,我们需要指定三角形的每个顶点各自对应纹理哪里部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样 (即采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpretation)。纹理坐标在x和y轴还是那个,范围为0和1之间。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0,0),也就是纹理图像的左下角,终始于(1, 1),即纹理图像的右上角。下图展示了我们如何把纹理坐标映射到三角形。
这里写图片描述
  在下面代码中, vertices数组就添加了每个顶点的纹理坐标。

void MyQGLWidget::initShader()
{
    glGenVertexArrays(NumVAOs, VAOs);
    glBindVertexArray(VAOs[Triangles]);
    GLfloat vertices[] = {
        // 位置                 // 纹理坐标
         0.5f, -0.5f, 0.0f,  1.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,  0.0f, 0.0f,   // 左下
         0.0f,  0.5f, 0.0f,  0.5f, 1.0f,   // 顶部
    };

    glGenBuffers(NumBuffers, Buffers);
    glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(vPosition);
    glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(vColor);
    glVertexAttribPointer(vColor, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3* sizeof(GLfloat)));

    const char*  vertexShaderCode =
        "#version 430 \n"
        ""
        "layout(location = 0) in vec3 vPosition;"
        "layout(location = 1) in vec2 textureCoord;"
        "out vec2 myTextureCoord;"
        ""
        "void main()"
        "{"
        "   gl_Position = vec4(vPosition, 1.0);"
        "   myTextureCoord = textureCoord;"
        "}";

    const char* fragmentShaderCode =
        "#version 430 \r \n"
        ""
        "in vec2 myTextureCoord;"
        "out vec4 fColor;"
        "uniform sampler2D myTexture;"
        ""
        "void main()"
        "{"
        "   fColor = texture(myTexture, myTextureCoord);"
        "}";
    GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

    const char* adapter[1];
    adapter[0] = vertexShaderCode;
    glShaderSource(vertexShaderID, 1, adapter, 0);

    adapter[0] = fragmentShaderCode;
    glShaderSource(fragmentShaderID, 1, adapter, 0);

    glCompileShader(vertexShaderID);

    GLint compiled;
    glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &compiled );
    if (!compiled)
    {
        std::cout<<"vertexShaderID";
        GLsizei len;
        glGetShaderiv( vertexShaderID, GL_INFO_LOG_LENGTH, &len );

        GLchar* log = new GLchar[len+1];
        glGetShaderInfoLog( vertexShaderID, len, &len, log );
        std::cerr << "Shader compilation failed: " << log << std::endl;
        delete [] log;
    }
    glCompileShader(fragmentShaderID);

    glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &compiled );
    if (!compiled)
    {
        std::cout<<"fragmentShaderID";
        GLsizei len;
        glGetShaderiv( fragmentShaderID, GL_INFO_LOG_LENGTH, &len );
        GLchar* log = new GLchar[len+1];
        glGetShaderInfoLog( fragmentShaderID, len, &len, log );
        std::cerr << "Shader compilation failed: " << log << std::endl;
        delete [] log;
     }

    GLuint programID = glCreateProgram();
    glAttachShader(programID, vertexShaderID);
    glAttachShader(programID, fragmentShaderID);

    glLinkProgram(programID);

    GLint linked;
    glGetProgramiv( programID, GL_LINK_STATUS, &linked );
    if (!linked)
        std::cout<<"Error";
    glUseProgram(programID);
   //GLint texLoc;
   //texLoc = glGetUniformLocation(programID, "myTexture");
   //glUniform1i(texLoc, 0); //GL_TEXTURE0,  关联
}

  上面代码中,glBindTexture绑定纹理,自动把纹理赋值给片段着色器程序的采样器。如上面的:uniform sampler2D myTexture。GLSL有一个供纹理对象使用的内建数据类型,叫做采样器(Sampler)。片段着色器程序中“fColor = texture(myTexture, myTextureCoord);”在着色器中使用纹理采样器来查询纹素值。
  如果GLSL传入多个纹理,可以利用gluniform1来显式地把纹理赋值给片段着色器程序的采样器。

运行结果:

这里写图片描述

着色语言语法

  这里用到了uniform存储限制符,我们详细介绍下。在着色器运行之前,uniform修饰符可以指定一个在应用程序中设置好的变量,它不会在图元 处理的过程中发生变化。uniform变量在所有可用的着色阶段之间都是共享的,它必须定义为全局变量。着色器无法写入到uniform变量,也无法改变它的值。
举例来说,我们在上面需要设置一个纹理采样器。因此可以声明一个uniform变量,将纹理对象传递给着色器中,在着色器中进行如下声明:

uniform sampler2D myTexture;

代码下载
OpenGL学习系列导航

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值