纹理

OGL纹理之纹理过滤器和多级渐进纹理mipmap 纹理过滤器 纹理图像映射到屏幕坐标系的图元中,很多时候不是和屏幕坐标系(像素)一一对应的,因此需要进行放大或缩小(尽量避免一个方向上放大一个方向上缩小,避免这个问题最好方法是不要使用扭曲的纹理坐标)以铺满对应的图元。纹理过滤器通过glTexParameteri函数来指定。 1)放大纹理过滤器 //GL_NEAREST最近点采样块有明显锯齿,GL_LINEAR线性过滤效果较好 glTexPa 阅读详情

继续 LearnOpenGL


纹理(Texture)。纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节;你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的3D的房子上,这样你的房子看起来就像有砖墙外表了。因为我们可以在一张图片上插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点。

纹理坐标

为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(译注:采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。

纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。下面的图片展示了我们是如何把纹理坐标映射到三角形上的。

我们为三角形指定了3个纹理坐标点。如上图所示,我们希望三角形的左下角对应纹理的左下角,因此我们把三角形左下角顶点的纹理坐标设置为(0, 0);三角形的上顶点对应于图片的上中位置所以我们把它的纹理坐标设置为(0.5, 1.0);同理右下方的顶点设置为(1, 0)。我们只要给顶点着色器传递这三个纹理坐标就行了,接下来它们会被传片段着色器中,它会为每个片段进行纹理坐标的插值。

纹理坐标看起来就像这样:
GLfloat texCoords[] = {
    0.0f, 0.0f, // 左下角
    1.0f, 0.0f, // 右下角
    0.5f, 1.0f // 上中
};

加载与创建纹理

和之前生成的OpenGL对象一样,纹理也是使用ID引用的。让我们来创建一个:
GLuint texture;
glGenTextures(1, &texture);
glGenTextures 函数首先需要输入生成纹理的数量,然后把它们储存在第二个参数的 GLuint 数组中(我们的例子中只是一个单独的 GLuint ),就像其他对象一样,我们需要绑定它,让之后任何的纹理指令都可以配置当前绑定的纹理:
glBindTexture(GL_TEXTURE_2D, texture);
纹理已经绑定了,我们纹理可以通过glTexImage2D 来生成:
    Image* img = new Image();
    img->initWithImageFile("wall.jpg");
    int width = img->getWidth();
    int height = img->getHeight();
    unsigned char* imgdata = img->getData();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
    glGenerateMipmap(GL_TEXTURE_2D);

函数很长,参数一个一个地讲解:

  • 第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
  • 第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
  • 第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。
  • 第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
  • 下个参数应该总是被设为0(历史遗留问题)。
  • 第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
  • 最后一个参数是真正的图像数据。

当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。然而,目前只有基本级别(Base-level)的纹理图像被加载了,如果要使用多级渐远纹理,我们必须手动设置所有不同的图像(不断递增第二个参数)。或者,直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。

生成了纹理和相应的多级渐远纹理后,释放图像的内存并解绑纹理对象是一个很好的习惯:
    //释放内存,解绑texture
    CC_SAFE_DELETE(img);
    glBindTexture(GL_TEXTURE_2D, 0);
生成一个纹理的过程应该看起来像这样:
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 为当前绑定的纹理对象设置环绕、过滤方式
...
// 加载并生成纹理
int width, height;
unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);

应用纹理

这部分我们会使用glDrawElements绘制矩形。我们需要告知OpenGL如何采样纹理,所以我们必须使用纹理坐标更新顶点数据:
GLfloat vertices[] = {
//     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
     0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
     0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
    -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
};

由于我们添加了一个额外的顶点属性,我们必须告诉OpenGL我们新的顶点格式:

glVertexAttribPointer(2, 2, GL_FLOAT,GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);

接着我们需要调整顶点着色器使其能够接受顶点坐标为一个顶点属性,并把坐标传给片段着色器:
// texture.vsh
attribute vec3 a_position;
attribute vec3 a_color;
attribute vec2 a_texcoord;

varying vec4 v_vertexColor;
varying vec2 v_textureCoord;

void main()
{
    gl_Position = vec4(a_position, 1);
    v_vertexColor = vec4(a_color, 1);
    v_textureCoord = vec2(a_texcoord.x, 1.0 - a_texcoord.y);
}

片段着色器也应该能访问纹理对象,但是我们怎样能把纹理对象传给片段着色器呢?GLSL有一个供纹理对象使用的内建数据类型,叫做采样器 (Sampler),它以纹理类型作为后缀,比如 sampler1D 、 sampler3D ,或在我们的例子中的 sampler2D 。我们可以简单声明一个 uniform sampler2D 把一个纹理添加到片段着色器中,这个uniform不需要手动赋值,下面详解。

//texture.fsh
varying vec4 v_vertexColor;
varying vec2 v_textureCoord;

uniform sampler2D u_myTexture; //纹理采样器, 它不需要手动赋值,在onDraw()中绑定纹理时自动赋值。
void main()
{
    gl_FragColor = texture2D(u_myTexture, v_textureCoord); //使用GLSL内建的texture2D函数来采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。
}
我们使用GLSL内建的texture2D 函数来采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。texture2D 函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。

在调用glDrawElements 之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器:
//绑定纹理, 它会自动把纹理赋值给片段着色器texture.fsh的采样器v_myTexture.因为一个纹理的默认纹理单元是0,它是默认的激活纹理单元。
    GL::bindTexture2D(textureId);
    
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

我们还可以把得到的纹理颜色与顶点颜色混合,来获得更有趣的效果。我们只需把纹理颜色与顶点颜色在片段着色器中相乘来混合二者的颜色:
gl_FragColor = v_vertexColor * texture2D(u_myTexture, v_textureCoord); 

全部代码如下:
//
//  Triangle.cpp
//  shaderTest
//
//  Created by MacSBL on 2016/12/15.
//
//

#include "OpenGLTexture.h"


bool OpenGLTexture::init()
{
    if (!Layer::init()) {
        return false;
    }
    
    GLfloat vertices[] = {
        //顶点        //颜色       //纹理坐标
        0.5, 0.5, 0,   1, 0, 0,    1.0, 1.0, //右上
        0.5, -0.5, 0,  0, 1, 0,    1.0, 0, //右下
        -0.5, -0.5, 0, 0, 0, 1,    0, 0, //左下
        -0.5, 0.5, 0,  0, 0, 0,    0, 1,//左上
    };
    GLuint indices[] = {
        0, 1, 3,
        1, 2, 3
    };
    
    //创建并绑定纹理
    //方法2
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    
    //纹理环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    //纹理过滤
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    //加载纹理
    Image* img = new Image();
    img->initWithImageFile("wall.jpg");
    int width = img->getWidth();
    int height = img->getHeight();
    unsigned char* imgdata = img->getData();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
    glGenerateMipmap(GL_TEXTURE_2D);
    //释放内存,解绑texture
    CC_SAFE_DELETE(img);
    glBindTexture(GL_TEXTURE_2D, 0);
    ///////////////////////////////////////////////////////////////////
    
    //1、绑定vao
    //顶点数组对象(Vertex Array Object)被绑定后,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    
    // 2. 把顶点数组复制到缓冲(vbo)中供OpenGL使用
    //使用glGenBuffers函数和一个缓冲ID生成一个VBO对象
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    //把之前定义的顶点数据复制到缓冲的内存中
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    //3, ebo
    GLuint ebo;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    //解析顶点数据, 应用到第1个顶点属性上
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8* sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // 颜色,应用到第2个属性上
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    //纹理坐标,应用到第3个属性上
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);
    
    //解除绑定vao和vbo
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————-
    auto program = new GLProgram;
    program->initWithFilenames("texture.vsh", "texture.fsh");
 //   program->initWithFilenames("texture.vsh", "texturemix.fsh");
    program->link();
    
    this->setGLProgram(program);
    
    return true;
}

void OpenGLTexture::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4 &parentTransform, uint32_t parentFlags)
{
    Layer::visit(renderer, parentTransform, parentFlags);
    _command.init(_globalZOrder);
    _command.func = CC_CALLBACK_0(OpenGLTexture::onDraw, this);
    Director::getInstance()->getRenderer()->addCommand(&_command);
}

void OpenGLTexture::onDraw()
{
    //获取当前node 的shader
    auto glprogram = getGLProgram();
    //需要在init中指定shader才能在这use
    glprogram->use();
    
    //绑定纹理, 它会自动把纹理赋值给片段着色器texture.fsh的采样器v_myTexture.因为一个纹理的默认纹理单元是0,它是默认的激活纹理单元。
    GL::bindTexture2D(textureId);
    
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

纹理单元

你可能会奇怪为什么sampler2D变量是个uniform,我们却不用glUniform给它赋值。使用glUniform1i,我们可以给纹理采样器分配一个位置值,这样的话我们能够在一个片段着色器中设置多个纹理。一个纹理的位置值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元,所以教程前面部分我们没有分配一个位置值。

纹理单元的主要目的是让我们在着色器中可以使用多于一个的纹理。通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理,只要我们首先激活对应的纹理单元。就像glBindTexture一样,我们可以使用glActiveTexture激活纹理单元,传入我们需要使用的纹理单元:

glActiveTexture(GL_TEXTURE0); //在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);

激活纹理单元之后,接下来的glBindTexture 函数调用会绑定这个纹理到当前激活的纹理单元,纹理单元 GL_TEXTURE0 默认总是被激活,所以我们在前面的例子里当我们使用 glBindTexture 的时候,无需激活任何纹理单元。

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。

我们仍然需要编辑片段着色器来接收另一个采样器。这应该相对来说非常直接了:
//在一个片段着色器中设置多个纹理
//texturemix.fsh
varying vec4 v_vertexColor;
varying vec2 v_textureCoord;

uniform sampler2D u_myTexture1; //纹理采样器,
uniform sampler2D u_myTexture2; //
void main()
{
    //最终输出颜色现在是两个纹理的结合。GLSL内建的mix函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值。如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。
    gl_FragColor =  mix(texture2D(u_myTexture1, v_textureCoord), texture2D(u_myTexture2, v_textureCoord), 0.4);
}

我们现在需要载入并创建另一个纹理;你应该对这些步骤很熟悉了。记得创建另一个纹理对象,载入图片,使用glTexImage2D生成最终纹理。

为了使用第二个纹理(以及第一个),我们必须改变一点渲染流程,先绑定两个纹理到对应的纹理单元,然后定义哪个uniform采样器对应哪个纹理单元:

/////////////////////////////////////////////////////////////
    //纹理单元, uniform采样器对应纹理单元
    GL::bindTexture2DN(0, textureId);
    glUniform1i(glGetUniformLocation(glprogram->getProgram(), "u_myTexture1"), 0);
    GL::bindTexture2DN(1, textureId2);
    glUniform1i(glGetUniformLocation(glprogram->getProgram(), "u_myTexture2"), 1);
    /////////////////////////////////////////////////////////////

如果纹理上下颠倒了, 有两个选择:
  1. 我们可以改变顶点数据的纹理坐标,翻转y值(用1减去y坐标)。
  2. 我们可以编辑顶点着色器来自动翻转y坐标,替换TexCoord的值为TexCoord = vec2(texCoord.x, 1.0f - texCoord.y);。
全部代码如下:
//
//  Triangle.cpp
//  shaderTest
//
//  Created by MacSBL on 2016/12/15.
//
//

#include "OpenGLTexture.h"


bool OpenGLTexture::init()
{
    if (!Layer::init()) {
        return false;
    }
    
    GLfloat vertices[] = {
        //顶点        //颜色       //纹理坐标
        0.5, 0.5, 0,   1, 0, 0,    1.0, 1.0, //右上
        0.5, -0.5, 0,  0, 1, 0,    1.0, 0, //右下
        -0.5, -0.5, 0, 0, 0, 1,    0, 0, //左下
        -0.5, 0.5, 0,  0, 0, 0,    0, 1,//左上
    };
    GLuint indices[] = {
        0, 1, 3,
        1, 2, 3
    };
    
    //创建并绑定纹理
    //////////////////////////////////////////////////////////
    //方法1
    auto sprite = Sprite::create("awesomeface.png");
    textureId2 = sprite->getTexture()->getName();
    //////////////////////////////////////////////////////////
    //方法2
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    
    //纹理环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    //纹理过滤
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    //加载纹理
    Image* img = new Image();
    img->initWithImageFile("wall.jpg");
    int width = img->getWidth();
    int height = img->getHeight();
    unsigned char* imgdata = img->getData();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
    glGenerateMipmap(GL_TEXTURE_2D);
    //释放内存,解绑texture
    CC_SAFE_DELETE(img);
    glBindTexture(GL_TEXTURE_2D, 0);
    ///////////////////////////////////////////////////////////////////
    
    //1、绑定vao
    //顶点数组对象(Vertex Array Object)被绑定后,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    
    // 2. 把顶点数组复制到缓冲(vbo)中供OpenGL使用
    //使用glGenBuffers函数和一个缓冲ID生成一个VBO对象
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    //把之前定义的顶点数据复制到缓冲的内存中
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    //3, ebo
    GLuint ebo;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    //解析顶点数据, 应用到第1个顶点属性上
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8* sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // 颜色,应用到第2个属性上
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    //纹理坐标,应用到第3个属性上
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);
    
    //解除绑定vao和vbo
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————-
    auto program = new GLProgram;
//    program->initWithFilenames("texture.vsh", "texture.fsh");
    program->initWithFilenames("texture.vsh", "texturemix.fsh");
    program->link();
    
    this->setGLProgram(program);
    
    return true;
}

void OpenGLTexture::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4 &parentTransform, uint32_t parentFlags)
{
    Layer::visit(renderer, parentTransform, parentFlags);
    _command.init(_globalZOrder);
    _command.func = CC_CALLBACK_0(OpenGLTexture::onDraw, this);
    Director::getInstance()->getRenderer()->addCommand(&_command);
}

void OpenGLTexture::onDraw()
{
    //获取当前node 的shader
    auto glprogram = getGLProgram();
    //需要在init中指定shader才能在这use
    glprogram->use();
    
    /////////////////////////////////////////////////////////////
    //纹理单元, uniform采样器对应纹理单元
    GL::bindTexture2DN(0, textureId);
    glUniform1i(glGetUniformLocation(glprogram->getProgram(), "u_myTexture1"), 0);
    GL::bindTexture2DN(1, textureId2);
    glUniform1i(glGetUniformLocation(glprogram->getProgram(), "u_myTexture2"), 1);
    /////////////////////////////////////////////////////////////
    
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}



openGL之API学习(一九三)glGenTextures 生成纹理单元,单元号不一定是连续的,但是没有使用的。单元号是相对GL_TEXTURE0的,对于单元号1,其实是GL_TEXTURE0+1。 void glGenTextures( GLsizei n, GLuint * textures); n Specifies the number of texture names to be generated. 指定生成纹理单元的数量 textures Specifies an array in which the gen... 阅读详情

相关推荐

OpenGL纹理贴图

OpenGL纹理1. 基本概念2. 纹理环绕方式3. 纹理过滤3.1 GL_NEAREST3.2 GL_LINEAR3.3 多级渐远纹理 (Mipmap)4. 纹理加载与创建4.1 纹理加载4.2 纹理创建4.3 纹理的使用 1. 基本概念 纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来表示物体的细节特征,希望绘制一个有足够多细节的物体,当然可以用足够多的顶点颜色来描述,但是无疑也增加了许多额外的开销;另一种办法就是使用纹理。 学习之前先展示一张实现的纹理图以更好理解纹理! 还是这张图为了将纹

牵LV小猪精的博客 8929

OpenGL:纹理

OpenGL:纹理

肖云鹤的博客 2968

OpenGL之纹理坐标及纹理映射

为了实现纹理贴图我们需要做三件事:将一张贴图加载到OpenGL中,提供纹理坐标和顶点(将纹理对应匹配到顶点上),并使用纹理坐标从纹理中进行取样操作取得像素颜色。由于三角形会被缩放、旋转、平移变换导致最后会以不同的结果投影显示到屏幕上,而且由于camera的不同操作看上去也会很不一样。GPU要做的就是让纹理紧跟三角形图元顶点的移动使其看上去真实(如果纹理看上去明显游离在三角形上产生错位就不真实了)。为实现这个效果开发者需要为每个顶点提供一系列纹理坐标。

一个小菜鸡的学习之路 4054

GLES2.0中文API-glGenerateMipmap

名称 glGenerateMipmap- 为纹理对象生成一组完整的mipmap C规范 void glGenerateMipmap(GLenum target); 参数 target     指定将生成mipmap的纹理对象绑定到的活动纹理单元的纹理目标。 必须是以下符号常量之一:GL_TEXTURE_2D或GL_TEXTURE_CUBE_MAP。 描述     glGenerat...

flycatdeng的专栏 7278

OpenGL纹理详解

OpenGL纹理详解 现实生活中,纹理最通常的作用是装饰我们的物体模型,它就像是贴纸一样贴在物体表面,使得物体表面拥有图案。但实际上在OpenGL中,纹理的作用不仅限于此,它可以用来存储大量的数据,一个典型的例子就是利用纹理存储地形信息。 纹理对象和参数 现代OpenGL中,纹理对象和顶点数组对象(VAO)及缓存对象(VBO)一样,需要调用glGenTextures函数生成。 生成纹理对象 同样的,纹理对象需要进行绑定。OpenGL中纹理可以分为1D,2...

水上漂的专栏 6460

Android OpenGL ES纹理总结、纹理坐标系说明、使用代码示例

#.纹理介绍: 可以简单想象成一张皮,可以贴在OpenGL空间中自己指定的区域之上,从而实现需要的视觉效果。 ##.使用纹理的好处: 如果想让图形看起来更真实,就必须有足够多的顶点,还要设置相应的颜色属性,会产生很多性能开销。 而如果是使用在指定的位置贴上一层纹理的方式,不需要非常多顶点,只需要纹理绘制满足需要,OpenGl中顶点位置与纹理对应位置关联正确。就可以达到需要的细节显示效果。 ##."需要蒙皮"位置与纹理的对应关系 纹理有自己内部的坐标系,纹...

u013914309的博客 3381

纹理单元、纹理对象、纹理类型、取样器对象

纹理对象包含着纹理贴图自身的数据,比如:纹素。 纹理对象并不是直接绑定到shader上的(事实上是采样阶段发生的地方),而是绑定到‘纹理单元’上,‘纹理单元’的索引会被传到shader中,因此shader是通过纹理单元得到纹理对象的。一般可以同时有多个可用的纹理单元,数量上限取决于显卡的容量。为了将一个纹理对象A绑定到纹理单元0上,首先你需要激活纹理单元0然后绑定纹理对象A。 应该是纹理对象直...

hankern的专栏 3306

OpenGL-对纹理的理解

看了《OpenGL编程指南》第九章纹理贴图之后,一开始还对纹理的理解思路还是有点乱,后面不断的阅读别人的博客之后才慢慢的有了更加清晰的认识,于是通过博客再进一步理顺一下思路。1.关于纹理的一些基本概念理解纹理:简单的说,纹理就是矩形的数据数组。例如颜色、亮度数据等,之所以复杂,是因为矩形纹理可以映射到非矩形的区域。 纹理映射:就是要实现如何把纹理像素映射到几何对象的每个点。2.纹理贴图的基本步骤2

yanglusheng的专栏 7101

OpenGL之——多纹理,混合纹理设置渲染

 实现下面三种纹理的组合和混合:  一、首先,获取纹理顶点坐标 #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoods; out vec2 TexCoods; void main(){ gl_Position = vec4(aPos, 1.0f); Te...

衣带渐宽人憔悴 9359

一文详解OpenGL采样API:glTexParameter glGenerateMipmap glTexImage2D

在OpenGL ES中 glTexParameter、glGenerateMipmap、glTexImage2D 这三个API经常用于纹理采样、生成Mipmap纹理 和 加载纹理图。 glTexParameter 用于加载纹理时的纹理采样 与 纹理拉伸算法 设定; glGenerateMipmap 用于生成Mipmap纹理; glTexImage2D 用于加载纹理图;

bjxiaxueliang的CODING小栈 4299

SAP PP后台配置实战:从BOM到工艺路线的全流程解析

本文详细解析了SAP PP模块后台配置的全流程,从BOM到工艺路线的关键步骤与实战技巧。通过分享生产计划、工作中心能力计划等核心配置经验,帮助企业优化生产管理流程,提升SAP系统实施效率。特别针对BOM配置、工艺路线设置等常见问题提供解决方案。

weixin_29300931的博客 1010

【OpenGL】纹理

在OpenGL中文手册上扩展和补充,采用QT进行开发,原生C++版本教程,请点击此处参考OpenGL CN本章节源码请。

qq_45280097的博客 1774

【OpenBMC 系列】4.启动流程 使用qume模拟ast2600-evb

1.安装openbmc适配的qume 2.启动bmc 3.使用bmc web

AiFool的博客 4459

Opengl实现纹理贴图

纹理贴图的步骤创建纹理对象,并为它指定一个纹理确定纹理如何应用到每个像素上启用纹理贴图功能绘制场景,提供纹理坐标和几何图形坐Tips:纹理坐标必须在RGBA模式下才能使用,在颜色索引模式下是未定义的行为一、创建纹理对象,并为它指定一个纹理static GLuint texName;glGenTextures(1, &texName); //创建1个纹理对象,&texName指向纹理...

Shenpotato的博客 1万+

32位ALU设计(verilog实现)

32位ALU设计 ​ 算术逻辑单元(arithmetic and logic unit) 是能实现多组算术运算和逻辑运算的组合逻辑电路,简称ALU。算术逻辑单元是中央处理器(CPU)的执行单元,是所有中央处理器的核心组成部分,由"And Gate"(与门) 和"Or Gate"(或门)构成的算术逻辑单元,主要功能是进行二位元的算术运算,如加减乘(不包括整数除法)。基本上,在所有现代CPU体系结构中,二进制都以补码的形式来表示。 设计思路 设计实现 仿真测试 文章目录32位ALU设计一、设计

Destiny的博客 4万+

Android 音频源码分析——AudioTrack设备选择

Android 音频源码分析——AndroidRecord录音(一) Android 音频源码分析——AndroidRecord录音(二) Android 音频源码分析——AndroidRecord音频数据传输流程 Android 音频源码分析——audioserver启动 Android 音频源码分析——AudioFlinger Android 音频源码分析——AudioTrack设备选择 基于Andorid9.0源码 以AudioTrack为例,梳理下输出设备选择流程。 音频设备选择的影响因素: Au

奋斗的菜鸟ing 3971

纹理基础——单张纹理&纹理属性笔记

使用,可以把一张图“黏”在模型表面,逐(纹素的名字是为了和像素进行区分)地控制模型的颜色。美术在建模时会在建模软件中利用纹理展开技术把存储在每个顶点上。定义了该顶点在纹理中对应的2D坐标,通常使用二位变量 (u, v) 来表示,u横坐标,v纵坐标,纹理映射坐标也被称为 UV 坐标。OpenGL纹理空间的原点位于左下角,DirextX纹理空间的原点位于左上角,Unity使用的纹理空间是符合OpenGL,原点位于左下角。

weixin_44746799的博客 2402

RK3588 DDR5 方案

RK3588 DDR5方案支持多种LPDDR5/LPDDR5X内存型号,包括美光、三星、海力士、江波龙和佰维等品牌。文档详细列出了已验证的DDR5内存规格,涵盖2GB-16GB容量范围,提供组织形式、封装类型和测试状态等信息。其中美光型号最全面,支持12GB/16GB大容量方案。部分型号处于测试验证阶段(T/A),需量产进一步确认性能。该方案为RK3588平台开发提供可靠的内存选型参考。

501846585_QQAndroid_java 1083

【openGLES】纹理

2D纹理是计算机图形学中最基础且应用最广泛的纹理类型,它本质上是一张二维图像(如.jpg、.png文件),通过特定的映射方式“贴”到3D模型的表面,从而为模型赋予丰富的表面细节(如颜色、图案、粗糙度等),而无需增加模型的几何复杂度。为了让你能快速掌握核心概念,我先把2D纹理的关键特性总结在下面的表格里:纹理坐标,通常称为 UV 坐标,是连接3D模型顶点与2D纹理图像的桥梁。它定义了模型表面的点与纹理像素(纹素)的对应关系:当纹理坐标超出 范围时,其行为由纹理环绕模式(Wrapping Mode) 决定,常

qq7835144的博客 1461

ASPICE标准快速掌握「总目录」

欢迎大家来到雪云飞星的《ASPICE标准快速掌握》,开始前博主先列出本专栏学习的大纲,同时这也可以作为大家学习ASPICE协议知识点的参考。本专栏可以帮助大家快速掌握如何在项目中使用ASPICE,ASPICE的学习通常仅需要半天时间左右,掌握方法后完全就可以结合快查表来指导具体项目的质量提升了。

雪云飞星的博客 3010

基于 STM32 的乒乓球发球机控制系统设计与实现

乒乓球发球机是一种能够按照预设参数自动、连续发出乒乓球的训练设备。本系统以 STM32F103C8T6 为主控核心,通过控制实现球速调节、实现落点与旋转方向控制、实现出球节奏控制,并配合 OLED 显示与按键人机交互,实现速度、频率、落点、旋转四大参数的可调发球训练。

weixin_42300449的博客 210

opengl 学习纹理

这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。这样没有任何效果,因为多级渐远纹理主要是使用在纹理被缩小的情况下的:纹理放大不会使用多级渐远纹理,为放大过滤设置多级渐远纹理的选项会产生一个GL_INVALID_ENUM错误代码。注意不要和纹理坐标搞混,纹理坐标是你给模型顶点设置的那个数组,OpenGL以这个顶点的纹理坐标数据去查找纹理图像上的像素,然后进行采样提取纹理像素的颜色。

cs821984831的博客 1932

PCL 改进快速欧式聚类【2025最新版】

在FEC: Fast Euclidean Clustering for Point Cloud Segmentation一文的基础上对算法进行改进。博客长期更新,本文最近更新时间为:2025年9月16日。

点云侠的博客 2570

纹理分析——图像纹理与纹理分析概述

纹理(Tuxture)一词最初指纤维物的外观。字典中对纹理的定义是“由紧密的交织在一起的单元组成的某种结构”。习惯上,把图像中这种局部不规则的,而宏观有规律的特性称之为纹理。因此,纹理是由一个具有一定的不变性的视觉基元,通称纹理基元,在给定区域内的不同位置上,以不同的形变及不同的方向重复地出现的一种图纹。人工纹理是某种符号的有序排列, 这些符号可以是线条、点、字母等,是有规则的。 自然纹理是具有重复排列现象的自然景象,如砖墙、森林、草地等图案,往往是无规则的。 提到纹理,人们自然会想到木质家具上的木纹,花布

钢铁男儿 2666

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

OpenGL--05 纹理、多级渐远纹理、纹理单元

GL_NEAREST产生颗粒状的图案,能够清晰看到组成纹理的像素,而GL_LINEAR能够产生更平滑的图案,很难看出单个的纹理像素。它通过为纹理生成一系列分辨率逐渐降低的图像(每个图像的大小是前一个的一半),在渲染时根据物体与观察者的距离选择合适的纹理级别(Level),从而避免纹理模糊或锯齿。距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终止于(1, 1),即纹理图片的右上角。,把图像转化为字节序列。

m0_69023437的博客 1378

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

深入解析纹理贴图——纹理压缩技术

3D计算机图形学离不开各种类型的纹理。纹理贴图可以极大地提高3D物体的视觉质量和细节水平,而不增加几何复杂度。简单的纹理是二维图像,该图像的单个像素称为纹素(texel)。事实上,纹理不仅可以存储颜色,还可以存储高度、法线方向、高光因子等每个纹理元素的信息。从上至下依次为漫反射贴图,法线贴图,位移贴图,来源:TEXTURE COMPRESSION TECHNIQUES随着现代游戏和3D应用程序体量越来越大,消耗了大量内存。

Mapmost的博客 2488

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

上一篇: shader 输入输出
下一篇: 矩阵、向量的变换
「已注销」
博客等级 码龄17年 6粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值