其实上一章就是在讲纹理了,不过上一章的目的仅是在想办法生成一张有纹理的矩形

一、纹理坐标与环绕方式

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

  • 我们很长的一段时间只需要考虑2D的图象,2D纹理坐标范围为(0~1),起始于左下角(0, 0),终止于右上角(1, 1)

使用纹理坐标获取纹理颜色就叫做采样(Sampling),如果我们取(0.5, 0.5)相当于就是获得图片最中间的点的颜色,但是如果我们采样超出了范围呢?例如对(1.5, 0.5)进行采样

是的并不会报错,而是通过一定的算法,映射到图片中的某个点(重复图像)又或者显示指定颜色,这个映射方法即是环绕方式,常规的环绕方式有如下4种:

  1. GL_REPEAT:默认行为,重复纹理图像
  2. GL_MIRRORED_REPEAT:重复纹理图像,镜像放置
  3. GL_CLAMP_TO_EDGE:坐标被约束在0到1之间,例如(1.5, 0.5)就会被强制改为(1, 0.5),超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果
  4. GL_CLAMP_TO_BORDER:超出的坐标为用户指定的边缘颜色

其中前面提到的每个选项都可以使用 glTexParameter 函数对单独的一个坐标轴设置(s, t, r,等价于x、y、z),前一章的代码中正有用到这个函数!

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

如果我们用第4中环绕方式,我们还需要指定边缘的颜色,否则默认为黑,代码如下

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

二、纹理过滤

如果你的电脑是2k的分辨率,那么很显然,桌面壁纸肯定也要选≥2k的,如果分辨率远低于这个值,那么桌面就会变得非常模糊,纹理也一样,我们上面只是指定了坐标,如果纹理贴图的分辨率非常低,那么OpenGL就需要知道怎样将纹理像素(Texture Pixel)映射到纹理坐标,并进行纹理过滤,图片变模糊其实就是纹理过滤的一种

  • 所谓的分辨率即使一个又一个的点,例如一张图片的分辨率为64 * 64,那么它就由那么多点组成,如果你想将这张图片显示在一个256 * 256的画布上,那么很显然就需要特定的算法去铺平整个画布了

有六种过滤方式:

  • GL_NEAREST:邻近过滤,默认过滤方式,对于画布中的每个像素,直接同等与离对应纹理坐标最近的像素
  • GL_LINEAR:线性过滤:对于画布中的每个像素,根据对应纹理坐标最近的4个点,插值得到当前像素的颜色(混合色)
  • GL_NEAREST_MIPMAP_NEAREST:使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
  • GL_LINEAR_MIPMAP_NEAREST:使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
  • GL_NEAREST_MIPMAP_LINEAR:在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
  • GL_LINEAR_MIPMAP_LINEAR:在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样

先看前2种过滤方式:(在过滤之前,画布中的每一个像素点会按照位置等比映射到纹理坐标中)

应该也猜到了,邻近过滤即是所谓的“马赛克风”,而线性过滤即是“模糊处理”

还记得前一章用了4个 glTexParameteri 函数嘛,后面2个即是设置其过滤方式

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

其中第二个参数表示当前过滤方式是在纹理进行放大(Magnify)还是缩小(Minify)时进行

再看后4种过滤方式:里面提到了多级渐远纹理(Mipmap),这其实属于一种优化

  • glGenerateMipmap:为当前绑定的纹理自动生成所有需要的多级渐远纹理

为什么需要优化:假设3D场景中有一块箱子,当这块箱子离你非常远的时候,那么这块箱子上的纹理理论上就不需要特别高的分辨率,然而OpenGL拿到的纹理资源分辨率可能会很高,这样OpenGL从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色,不但如此对这块箱子使用高分辨率的纹理还会比较占内存

优化的方法:即是利用多级渐远纹理这个手段,也就是对于一张纹理生成一系列的纹理图象,每一个纹理图像都是前一个的二分之一,根据物体相对玩家的距离,openGL选择最适合对应大小的两张多级渐远纹理,之后进行的操作就取决过滤方式的设置了

很明显,这4种过滤方式只适用于缩小过滤

三、纹理单元

接一下上一章的代码,其实有两个问题

  1. 前面讲了采样器(sampler),是一个供纹理对象使用的内建数据类型,在片段着色器中,我们定义的sampler2D变量是个uniform,然而我们却并没有使用glUniform给其赋值
  2. 仔细看下,我们生成的图片,居然是上下颠倒的,越是不对称的图象越容易发现

对于①,还记得顶点着色器的layout嘛,它代表着对应属性的位置值,这里的sampler其实和它一样,也是位置值,一个纹理的位置值通常称为一个纹理单元(Texture Unit),当你不使用glUniform对其赋值的时候,默认为0

纹理单元的主要目的就是让我们在着色器中可以使用多于一个的纹理,默认激活位置值0的纹理单元,其它纹理单元需要手动激活

现在可以稍微修改代码,混合两张纹理,如下:

一样的,我们生成两个纹理图象,那么同样的代码再来一份就可以了,其中textureA和textureB即是绑定的两张纹理

int picWidth, picHeight;
glGenTextures(1, &textureA);
glBindTexture(GL_TEXTURE_2D, textureA);
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);
unsigned char* image = SOIL_load_image("timg.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);glGenTextures(1, &textureB);
glBindTexture(GL_TEXTURE_2D, textureB);
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 = SOIL_load_image("minecraftA.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);

可以看到操作是一样的,紧接着修改片段着色器

#version 330 core
out vec4 color;
in vec4 colorIn;
in vec2 texIn;
uniform sampler2D texOutA;
uniform sampler2D texOutB;
void main()
{color = mix(texture(texOutA, texIn), texture(texOutB, texIn), 0.8);
}
  • mix(a, b, x):其中a和b为颜色参数,第三个值为插值参数,设这个参数为x%,那么这个mix方法最终就会返回 x% 的第二个颜色 + (1-x%)的第一个颜色(显然如果这个值为1相当于直接取第二个参数b)

渲染流程修改如下:

glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(glGetUniformLocation(ourShader.Program, "texOutA"), 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUniform1i(glGetUniformLocation(ourShader.Program, "texOutB"), 1);
  • glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1):设置正确的位置值(纹理单元)
  • glActiveTexture(GL_TEXTURE0):激活对应的纹理单元,激活的纹理单元取决于参数最后的数字,我们也可以使用类似于(GL_TEXTURE0 + 1)的方法来表示 GL_TEXTURE1

对于②,现在可以成功运行并且生成一张上下颠倒的图片,出现这种情况的原因是OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部,也就是第一个像素的位置

我们现在可以暂时通过调整顶点着色器来解决,当然方法非常暴力,将y轴拿1减去就好了

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec4 color;
layout (location = 2) in vec2 texture;
out vec4 colorIn;
out vec2 texIn;
void main()
{gl_Position = vec4(position, 1.0);colorIn = color;texIn = vec2(texture.x, 1.0f - texture.y);
}

完整代码:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include<opengl/freeglut.h>
#include<SOIL.h>void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
const GLuint WIDTH = 800, HEIGHT = 600;int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);glfwSetKeyCallback(window, key_callback);glewExperimental = GL_TRUE;glewInit();int width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);Shader shaderYellow("VShader.txt", "FShaderY.txt");GLfloat vertices[] ={-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f};GLuint indices[] ={0, 1, 2,        //用前3个顶点绘制第一个三角形1, 2, 3         //用后3个顶点绘制第二个三角形};GLuint VBO, EBO, VAO, textureA, textureB;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));glEnableVertexAttribArray(2);int picWidth, picHeight;glGenTextures(1, &textureA);glBindTexture(GL_TEXTURE_2D, textureA);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);unsigned char* image = SOIL_load_image("timg.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);SOIL_free_image_data(image);glGenTextures(1, &textureB);glBindTexture(GL_TEXTURE_2D, textureB);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 = SOIL_load_image("minecraftA.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);SOIL_free_image_data(image);glBindTexture(GL_TEXTURE_2D, 0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);while (!glfwWindowShouldClose(window)){glfwPollEvents();glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);shaderYellow.Use();glBindTexture(GL_TEXTURE_2D, textureA);glUniform1i(glGetUniformLocation(shaderYellow.Program, "texOutA"), 0);glActiveTexture(GL_TEXTURE0 + 1);glBindTexture(GL_TEXTURE_2D, textureB);glUniform1i(glGetUniformLocation(shaderYellow.Program, "texOutB"), 1);glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glBindVertexArray(0);glfwSwapBuffers(window);}glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);glfwTerminate();return 0;
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

其中其中 Shader.h 可以在上上章找到代码,没有改动,两个着色器的代码在上面

四、扩展

①我们代码中还是用了颜色属性,但是上面并没有体现出来,只有纹理

其实只需要把纹理颜色和顶点颜色在片段着色器中相乘就可以做到混合的效果了,代码改动和效果如下:

#version 330 core
out vec4 color;
in vec2 texIn;
in vec4 colorIn;
uniform sampler2D texOutA;
uniform sampler2D texOutB;
void main()
{color = mix(texture(texOutA, texIn), texture(texOutB, texIn), 0.8) * colorIn;
}

②上面代码中的mix参数是写死的0.8,我们可以尝试控制这个值,实现可以通过上和下键来改变mc游戏截图的可见度

片段着色器修改如下:

#version 330 core
out vec4 color;
in vec2 texIn;
in vec4 colorIn;
uniform sampler2D texOutA;
uniform sampler2D texOutB;
uniform float mixVal;
void main()
{color = mix(texture(texOutA, texIn), texture(texOutB, texIn), mixVal);
}

完整代码:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include<opengl/freeglut.h>
#include<SOIL.h>void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
const GLuint WIDTH = 800, HEIGHT = 600;
GLfloat mixVal = 0.2f;int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);glfwSetKeyCallback(window, key_callback);glewExperimental = GL_TRUE;glewInit();int width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);Shader shaderYellow("VShader.txt", "FShaderY.txt");GLfloat vertices[] ={-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f};GLuint indices[] ={0, 1, 2,        //用前3个顶点绘制第一个三角形1, 2, 3         //用后3个顶点绘制第二个三角形};GLuint VBO, EBO, VAO, textureA, textureB;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));glEnableVertexAttribArray(2);int picWidth, picHeight;glGenTextures(1, &textureA);glBindTexture(GL_TEXTURE_2D, textureA);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);unsigned char* image = SOIL_load_image("timg.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);SOIL_free_image_data(image);glGenTextures(1, &textureB);glBindTexture(GL_TEXTURE_2D, textureB);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 = SOIL_load_image("minecraftA.jpg", &picWidth, &picHeight, 0, SOIL_LOAD_RGB);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);SOIL_free_image_data(image);glBindTexture(GL_TEXTURE_2D, 0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);while (!glfwWindowShouldClose(window)){glfwPollEvents();glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);shaderYellow.Use();glBindTexture(GL_TEXTURE_2D, textureA);glUniform1i(glGetUniformLocation(shaderYellow.Program, "texOutA"), 0);glActiveTexture(GL_TEXTURE0 + 1);glBindTexture(GL_TEXTURE_2D, textureB);glUniform1i(glGetUniformLocation(shaderYellow.Program, "texOutB"), 1);glUniform1f(glGetUniformLocation(shaderYellow.Program, "mixVal"), mixVal);glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glBindVertexArray(0);glfwSwapBuffers(window);}glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);glfwTerminate();return 0;
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);if (key == GLFW_KEY_UP && action == GLFW_PRESS){mixVal += 0.1f;if (mixVal >= 1.0f)mixVal = 1.0f;}if (key == GLFW_KEY_DOWN && action == GLFW_PRESS){mixVal -= 0.1f;if (mixVal <= 0.0f)mixVal = 0.0f;}
}

OpenGL基础9:纹理相关推荐

  1. OpenGL基础54:点光源阴影

    前置: OpenGL基础53:阴影映射(下) 一.万象阴影贴图 之前成功实现了平行光阴影,生成阴影贴图时使用的矩阵是正交矩阵,若是想要实现点光源的阴影效果,那么理论上只需要修改投影矩阵为透视矩阵就好了 ...

  2. OpenGL基础53:阴影映射(下)

    接上文:OpenGL基础52:阴影映射(上) 五.阴影失真 按照上文的计算的结果,一个很明显的问题是:对于参与计算深度贴图的物体,其表面可以看到这样的栅格状的阴影,这种常见的错误表现也叫做阴影失真(S ...

  3. OpenGL基础50:HDR

    一.HDR与LDR 由于显示器只能显示值为0.0到1.0间的颜色,因此当数据存储在帧缓冲(Framebuffer)中时,亮度和颜色的值也是默认被限制在0.0到1.0之间的,这个颜色范围即是LDR(Lo ...

  4. OpenGL基础49:高度贴图(下)

    接上文:OpenGL基础48:高度贴图(上) 四.陡峭视差映射 上文计算纹理偏移的方法是最简单的,但问题也比较大:当你斜视贴图的时候可以看到非常明显的错误 这里有另一种方法,这种方法类似于找连续函数在 ...

  5. OpenGL基础46:切线空间

    到这里,关于OpenGL基础的了解要接近尾声了,上一个节点是<OpenGL基础25:多光源>.在此章之后,学习openGL的各种教程的同时,可以转战想要了解的渲染引擎,也可以去github ...

  6. OpenGL基础45:光照矫正(下)之Gamma校正

    接上文:OpenGL基础44:光照矫正(上) 四.Gamma矫正 4.1.人的视觉特性 和很多错视图一样,对于下面这张灰阶图,如果1表示纯白,0表示纯黑,那么这张图片的哪个位置代表的是0.5,也就是自 ...

  7. OpenGL基础44:光照矫正(上)

    对于openGL的API,倒是没有必要花太多时间,重点应该还是在着色器上 一.采样器.glActiveTexture和glBindTexture 在之前测试简单光照时可能出现的两个问题,尽管它们可能不 ...

  8. OpenGL基础43:抗锯齿

    一.走样与反走样 走样(Aliasing)就是锯齿化,反走样(Anti-aliasing)就是抗锯齿 只要玩过游戏,那么都应该对抗锯齿不陌生,不少游戏也都有关于抗锯齿的设置 如上图,放大的部分能很明显 ...

  9. OpenGL基础40:Uniform缓冲

    前置:OpenGL基础39:GLSL内建变量与接口块 想想之前代码,glUniform()和glGetUniformLocation()的使用数量是不是过于频繁了,对于每个着色器的每一个uniform ...

最新文章

  1. JavaScript中的异步梳理(0)
  2. “throw”和“throw ex”之间有区别吗?
  3. python资料库-Python操作三大主流数据库
  4. 概述VB.NET正则表达式简化程序代码
  5. Hadoop系列二:Hadoop单节点伪分布部署并执行mapreduce示例wordcount
  6. 有监督回归:鲁棒学习
  7. mysql自动提交 dcl语句_MySQL基础:DCL语句总结
  8. DSP2812程序执行过程
  9. 汇编语言 向内存0:200~0:23f依次传送数据0~63(3fh)
  10. java httpserver 多个接口_多个Servlet之间数据共享实现方案
  11. 蓝牙协议栈中的 OSAL
  12. 面试—每日一题(2)
  13. 表格列数太多 页面怎么设计_B端产品设计规范分享
  14. tjh_pipeline_tools Maya工具盒 1.2.1 下载及教程 动画影视团队工具共享系统
  15. Java程序员面试简历模板(30套简历模板+300套简历)
  16. Java:Eclipse下载安装教程,以及Eclipse 安装汉化包的方法
  17. DelayQueue用例
  18. 玩转流量,天下无锅——IT运维人员的九阳神功(上)
  19. 20个你应该知道的iPhone 7实用小技巧
  20. web前端面试总结(自认为还算全面哈哈哈哈哈!!!!)

热门文章

  1. 讯飞输入法更新10.0版本 上线全新A.I.语音输入引擎
  2. 基于TMS320VC5507的语音识别系统实现
  3. 百度语音识别最新资讯
  4. python强转字符串_在Python 3中将Exception转换为字符串
  5. jsch 长连接_广濑连接器DF13
  6. 运筹学状态转移方程例子_逆转的薛定谔方程,美俄科学家实现量子时间“倒流”,令人兴奋...
  7. 用c++实现简单单链表,双链表,二叉树类
  8. ffmpeg系列-编译
  9. 镜头分割:像素域方法综述
  10. java面向对象相关选择题_java面向对象练习题一