直接上代码然后作检讨。
#define _CRT_SECURE_NO_WARNINGS
#include<glad/glad.h>
#include<GLFW/glfw3.h>
#include<iostream>
#include<sstream>
#include<fstream>
#define STB_IMAGE_IMPLEMENTATION
#include<stb_image.h>
void processInput(GLFWwindow*);
void sourceInput(unsigned int& vertexShader, unsigned int& fragmentShader, unsigned int& shaderProgram, const char* vertexShaderPath, const char* fragmentShaderPath);
int main() {
stbi_set_flip_vertically_on_load(true);
/// <summary>
/// Part 1: Initialize and configure GLFW
/// </summary>
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 800, "Learn", NULL, NULL);
if (window == NULL) {
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cerr << "Failed to initialize GLAD" << std::endl;
return -1;
}
/// <summary>
/// Part 2: Set up vertex data (and buffer(s)) and configure vertex attributes
/// </summary>
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
float vertices[] = {
-0.5f, -0.5f, 0.0f, 1.0f,0.0f,0.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f,1.0f,0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 0.0f,0.0f,1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 0.0f,0.5f,0.0f, 0.0f, 1.0f
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
unsigned int indices[] = {
0,1,2,
2,3,0
};
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glBindVertexArray(0);
/// <summary>
/// Part 3: Build and compile our shader program
/// </summary>
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
unsigned int shaderProgram = glCreateProgram();
sourceInput(vertexShader, fragmentShader, shaderProgram, "VertexShader.glsl", "FragmentShader.glsl");
unsigned int texture0;
unsigned int texture1;
glGenTextures(1, &texture0);
glGenTextures(1, &texture1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
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_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
{
int width, height, nrChannels;
unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cerr << "Failed to load texture" << std::endl;
}
}
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture1);
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_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
{
int width, height, nrChannels;
unsigned char* data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cerr << "Failed to load texture" << std::endl;
}
}
/// <summary>
/// Part 4: Render loop
/// </summary>
glfwSetFramebufferSizeCallback(window,
[](GLFWwindow* window, int width, int height) {glViewport(0, 0, width, height); });
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(glGetUniformLocation(shaderProgram, "texture0"), 0);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 1);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}
void sourceInput(unsigned int& vertexShader, unsigned int& fragmentShader, unsigned int& shaderProgram, const char* vertexShaderPath, const char* fragmentShaderPath)
{
std::ifstream vertexShaderFile;
std::ifstream fragmentShaderFile;
std::string vertexShaderStr;
std::string fragmentShaderStr;
vertexShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fragmentShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
std::stringstream vertexShaderStringStream;
std::stringstream fragmentShaderStringStream;
vertexShaderFile.open(vertexShaderPath);
fragmentShaderFile.open(fragmentShaderPath);
vertexShaderStringStream << vertexShaderFile.rdbuf();
fragmentShaderStringStream << fragmentShaderFile.rdbuf();
vertexShaderFile.close();
fragmentShaderFile.close();
vertexShaderStr = vertexShaderStringStream.str();
fragmentShaderStr = fragmentShaderStringStream.str();
}
catch (const std::exception&)
{
std::cerr << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vertexShaderSource = vertexShaderStr.c_str();
const char* fragmentShaderSource = fragmentShaderStr.c_str();
std::cout << vertexShaderSource << std::endl << std::endl;
std::cout << fragmentShaderSource << std::endl << std::endl;
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(vertexShader);
{
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
}
glCompileShader(fragmentShader);
{
int success;
char infoLog[512];
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
}
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
{
int success;
char infoLog[512];
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cerr << "ERROR::PROGRAM::LINK_FAILED\n" << infoLog << std::endl;
}
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
//vertexshader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec4 vertexColor;
out vec2 texCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(aColor, 1.0);
texCoord = aTexCoord;
}
//fragmentshader.glsl
#version 330 core
in vec4 vertexColor;
in vec2 texCoord;
out vec4 FragColor;
uniform sampler2D texture0;
uniform sampler2D texture1;
void main()
{
FragColor = mix(texture(texture0, texCoord),texture(texture1,texCoord),0.2)*vertexColor;
}
minifying, magnifying, downscaled, supersampling, texels, sampler
纹理的使用方式和其他对象有所区别:
unsigned int texture;
glGenTexture(1,&texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,texture);
glTexParameteri(...);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,data);
glGenerateMipmap(GL_TEXTURE_2D);
此前已经学过的用一个宏管理的:VBO、EBO、shader。
不用宏管理的:VAO、program。
纹理使用两个宏管理,一个是GL_TEXTURE_xD,另一个是GL_TEXTUREx,当glActiveTexture()将一个宏设为活动时,所有使用GL_TEXTURE_2D修改的属性都属于那个被绑定的宏。这相当于每个纹理都与一个宏GL_TEXTUREx关联。这个宏称作texture units
生成、active、绑定、配置
纹理坐标
值取从0到1,有1、2、3D纹理,分别具有坐标轴s、t、r,在设置参数时使用GL_TEXTURE_WRAP_S+GL_REPEAT设置超出0到1的纹理坐标的处理方式。
降采样和过采样
采样是在纹理上给fragment片段采集颜色数据。
当多个片段采集一个纹理像素点texel的情况下,称为降采样,t/f<1(或者在原本完美贴合的情况下放大纹理,magnifying)
当一个片段需要采集多个texel的情况下,称为过采样,t/f>1(缩小纹理,downscaling,minifying,supersampling)
降采样其实并不需要特殊处理,可以选择GL_NEAREST或GL_LINEAR规则采样插值。
过采样情况可能会出现的较多并且会使用mipmap技术、之上的各项异性过滤技术等。
过采样容易出现的原因是远处三角形在rasterization阶段形成的片段数会很少,于是相比纹理大小形成明显区别。
(低频采样方式(少片段)采集高频信号(多texel)会导致严重走样)
如果使用mipmap进行插值将会能够选择远近两个mipmap的选择规则,但是两mipmap之间默认线性插值。
使用GL_TEXTURE_MIN_FILTER和GL_TEXTURE_MAG_FILTER宏指定修改的参数。
stb头文件
这是个C风格的all in one header工具库。使用细节很多所以不考虑。
GLSL使用纹理
//vertexshader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec4 vertexColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(aColor, 1.0);
TexCoord = aTexCoord;
}
//fragmentshader.glsl
#version 330 core
in vec4 vertexColor;
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D Texture1;
void main()
{
FragColor = texture(Texture1, TexCoord);
}
多了一个VAA自然要在vertexshader中体现。(layout in、out)
在fragmentshader中使用纹理需要一个uniform的sampler2D对象和texture()函数。
该sampler2D对象不一定需要手动管理,在只有一个纹理时默认为GL_TEXTURE0。
当需要修改它对应的纹理时,直接将数字赋给它,比如要设置为纹理1就赋值1。
//render loop中
glUniformi(glGetUniformLocation(shaderProgram,"Texture1"),1);
颜色计算和纹理叠加
RGB颜色的叠加,其计算方式基本是用乘号,后面的各种颜色叠加都是用该法。但是为了特殊效果可以用其他方式。
纹理叠加用mix,看着像计算了透明但事实上没有计算!alpha测试在下一个环节的shader里。
检讨
用接近4h的时间,但事实上在学的时间应该就一个多小时,其他时间都在处理两个纹理报错的情况,即使用copilot也没能排除。
然后发现第一个问题是由于在VBO中添加了纹理坐标但是没有enable纹理坐标的VAA;第二个是把材质名称后缀打错了。
首先是两个重要事实:opengl的debug真的很困难,纯靠程序员经验;ai在低级错误上往往比较难排查,使用ai的方法还是需要学习的。
其次是自我批评:感觉找不到错就及时止损过一段时间再回来找,以及写代码要动脑子。
(可能要想点办法能主动动脑)

comment 评论区
star_outline 咱快来抢个沙发吧!