当前位置: 首页 > news >正文

OpenGL Chan视频学习-10 Dealing with Errors in OpenGL

bilibili视频链接:

【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p=5&vd_source=44b77bde056381262ee55e448b9b1973

函数网站:

docs.gl

说明:

1.之后就不再单独整理网站具体函数了,网站直接翻译会更直观也会有更多注意点。直接通过csdn索引查找反而会慢。

2.代码区域会单独注释功能参数返回值和相关注意事项。

3.课程学习从4-本节,如果有些函数没有注释可以看专栏里面的前面发表的文章,一般有解释。

4.如果觉得代码注释白色字体不太直观可以直接copy到相应软件看

5.有两种版本的可供查看:注释全面的和注释简洁版的,可以在索引里面找到相关代码查看。

6.希望能帮到你。

7.有错误请跟我说明一下,可能整理的时候没有检查好

一、知识点整理

1.1 检查OpenGL错误

1.1.1方法一 glGetError

1.1.1.1网址

glGetError - OpenGL 2 - docs.glhttps://docs.gl/gl2/glGetError

1.1.1.2示例
static void GLClearError()
{//功能:检测OpenGL错误//glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。//注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。while (glGetError() != GL_NO_ERROR);
}static void GLCheckError()
{//功能:检测OpenGL错误,输出错误信息//while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。while (GLenum error = glGetError()){std::cout << "[OpenGL Error] (" << error << "): " << std::endl;}
}

为什么要在while里面调用?

在代码中,glGetError()函数用于检测OpenGL错误,它会返回最后一次发生的错误代码,并且每次调用该函数时会清除之前的错误。因此,如果只是调用一次glGetError(),可能无法捕获所有累积的错误,因为之前的错误会被清除。

通过将glGetError()放在一个while循环中,可以不断地检查和清除OpenGL错误,直到返回GL_NO_ERROR为止。这样可以确保在执行OpenGL操作之前,所有的错误都被捕获和处理,从而帮助开发者调试和确保程序的正确性。

请注意,这个循环通常会消耗CPU资源,因为它会一直检查错误直到没有错误为止,所以在实际应用中可能需要谨慎使用。

1.1.1.3运行结果

1.1.1.4解决方法

1.log

2.将十进制转换成十六进制。右键点击error

3.进入#include <GL/glew.h>搜索500

无效的枚举

1.1.1.5注意

应该在循环中调用确保得到所有错误

1.1.1.6缺点

不显示错误行,必须自行在每个函数之前调用并检测错误

1.1.2方法二

1.1.2.1代码

ASSERT宏

//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
//这个宏接受一个参数 x,它是一个条件表达式。
//if(!(x)) __debugbreak();: 这是宏的实现部分。
// 它检查参数 x 是否为 false。
// 如果是,__debugbreak(); 会被调用,这通常会导致程序在当前位置中断,进入调试器。
#define ASSERT(x) if(!(x)) __debugbreak();

函数

static void GLClearError()
{//功能:检测OpenGL错误//glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。//注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。while (glGetError() != GL_NO_ERROR);
}static bool GLLogError()
{//功能:检测OpenGL错误,输出错误信息//while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。while (GLenum error = glGetError()){std::cout << "[OpenGL Error] (" << error << "): " << std::endl;return false;}return true;
}

int main修改

GLClearError();
//功能:绘制三角形
glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);
ASSERT(GLLogError());
1.1.2.2运行结果

1.1.3方法二改进

1.1.3.1代码

//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
//这个宏接受一个参数 x,它是一个条件表达式。
//if(!(x)) __debugbreak();: 这是宏的实现部分。
// 它检查参数 x 是否为 false。
// 如果是,__debugbreak(); 会被调用,这通常会导致程序在当前位置中断,进入调试器。
#define ASSERT(x) if(!(x)) __debugbreak();
//功能:定义了一个宏 GLCall(x),用于调试 OpenGL 应用程序时检查和处理 OpenGL 错误。
//x 是该宏接受的一个参数,通常是一个 OpenGL 函数调用。
//GLClearError();: 宏展开后首先会调用 GLClearError() 函数,清除 OpenGL 错误缓冲中的所有错误。
//x;: 接着是传入的 OpenGL 函数调用。
// 例如,如果宏被调用为 GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));,
// 那么这里将展开为实际的 OpenGL 函数调用 glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);。
//ASSERT(GLLogError(#x, __FILE__, __LINE__));: 
// #x 将 OpenGL 函数调用转换为字符串。
// __FILE__ 和 __LINE__ 是预定义的宏,分别包含当前源文件名和行号。
// GLLogError() 函数会输出任何检测到的错误信息。如果错误发生,ASSERT 宏将导致程序中断并进入调试器。// 整体思路:
//1.错误清除:
//GLClearError() 函数通过循环调用 glGetError() 清除 OpenGL 错误缓冲中的所有错误,直到 glGetError() 返回 GL_NO_ERROR。
//2.OpenGL 函数调用:
//x; 是传入的实际 OpenGL 函数调用,比如 glDrawElements、glBindBuffer 等。
//3.错误检查和断言:
//GLLogError() 函数再次调用 glGetError() 来检查是否有新的错误发生。如果检测到错误,它会将错误代码和错误信息输出到标准输出,并返回 false。
//ASSERT(GLLogError()); 宏用于验证 GLLogError() 的返回值。如果返回值为 false(即有错误发生),程序将中断并进入调试器,这样开发者可以更容易地找到并修复问题。
#define GLCall(x) GLClearError();\x;\ASSERT(GLLogError(#x,__FILE__,__LINE__));

函数

static void GLClearError()
{//功能:检测OpenGL错误//glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。//注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。while (glGetError() != GL_NO_ERROR);
}//参数:1.function: 发生错误的函数名
//     2.file: 发生错误的文件名
//     3.line: 发生错误的行号
//功能:输出OpenGL错误信息
static bool GLLogError(const char* function, const char* file, int line)
{//功能:检测OpenGL错误,输出错误信息//while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。while (GLenum error = glGetError()){std::cout << "[OpenGL Error] (" << error << "): " << function << " " << file << ":" << line << std::endl;return false;}return true;
}

int main调用修改

//功能:绘制三角形
GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));
1.1.3.2运行结果

1.1.4方法三——glDebugMessageCallback
1.1.4.1网址

glDebugMessageCallback - OpenGL 4 - docs.glhttps://docs.gl/gl4/glDebugMessageCallback

二、完整代码

2.1完全注释代码

#include <GL/glew.h>
#include <GLFW/glfw3.h>#include<iostream>
#include<fstream>
#include<string>
#include<sstream>//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
//这个宏接受一个参数 x,它是一个条件表达式。
//if(!(x)) __debugbreak();: 这是宏的实现部分。
// 它检查参数 x 是否为 false。
// 如果是,__debugbreak(); 会被调用,这通常会导致程序在当前位置中断,进入调试器。
#define ASSERT(x) if(!(x)) __debugbreak();
//功能:定义了一个宏 GLCall(x),用于调试 OpenGL 应用程序时检查和处理 OpenGL 错误。
//x 是该宏接受的一个参数,通常是一个 OpenGL 函数调用。
//GLClearError();: 宏展开后首先会调用 GLClearError() 函数,清除 OpenGL 错误缓冲中的所有错误。
//x;: 接着是传入的 OpenGL 函数调用。
// 例如,如果宏被调用为 GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));,
// 那么这里将展开为实际的 OpenGL 函数调用 glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);。
//ASSERT(GLLogError(#x, __FILE__, __LINE__));: 
// #x 将 OpenGL 函数调用转换为字符串。
// __FILE__ 和 __LINE__ 是预定义的宏,分别包含当前源文件名和行号。
// GLLogError() 函数会输出任何检测到的错误信息。如果错误发生,ASSERT 宏将导致程序中断并进入调试器。// 整体思路:
//1.错误清除:
//GLClearError() 函数通过循环调用 glGetError() 清除 OpenGL 错误缓冲中的所有错误,直到 glGetError() 返回 GL_NO_ERROR。
//2.OpenGL 函数调用:
//x; 是传入的实际 OpenGL 函数调用,比如 glDrawElements、glBindBuffer 等。
//3.错误检查和断言:
//GLLogError() 函数再次调用 glGetError() 来检查是否有新的错误发生。如果检测到错误,它会将错误代码和错误信息输出到标准输出,并返回 false。
//ASSERT(GLLogError()); 宏用于验证 GLLogError() 的返回值。如果返回值为 false(即有错误发生),程序将中断并进入调试器,这样开发者可以更容易地找到并修复问题。
#define GLCall(x) GLClearError();\x;\ASSERT(GLLogError(#x,__FILE__,__LINE__));static void GLClearError()
{//功能:检测OpenGL错误//glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。//注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。while (glGetError() != GL_NO_ERROR);
}//参数:1.function: 发生错误的函数名
//     2.file: 发生错误的文件名
//     3.line: 发生错误的行号
//功能:输出OpenGL错误信息
static bool GLLogError(const char* function, const char* file, int line)
{//功能:检测OpenGL错误,输出错误信息//while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。while (GLenum error = glGetError()){std::cout << "[OpenGL Error] (" << error << "): " << function << " " << file << ":" << line << std::endl;return false;}return true;
}//功能:定义ShaderProgramSource结构体,用于存储着色器代码
struct ShaderProgramSource
{std::string VertexSource;std::string FragmentSource;
};//功能:解析着色器代码文件。
static ShaderProgramSource ParseShader(const std::string& filepath)
{//功能:打开文件流std::ifstream stream(filepath);//定义着色器类型enum  class ShaderType{NONE=-1,VERTEX=0,FRAGMENT=1};//该变量用于存储着色器代码std::string line;//该变量用于存储着色器类型std::stringstream ss[2];//该变量是当前着色器类型ShaderType type = ShaderType::NONE;//功能:读取文件中的每一行内容,直到文件结束while (getline(stream, line)){//如果当前行包含#shader,则说明接下来是着色器代码if (line.find("#shader") != std::string::npos){//如果当前行包含vertex,则说明接下来是顶点着色器代码if (line.find("vertex") != std::string::npos){type = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos){type = ShaderType::FRAGMENT;}}else{//否则,将当前行添加到对应着色器代码的stringstream中ss[(int)type] << line << '\n';}}//返回ShaderProgramSource结构体return { ss[0].str(), ss[1].str() };
}//功能:编译着色器代码
static unsigned int CompilesShader(unsigned int type, const std::string& source)
{//功能:创建着色器对象unsigned int id = glCreateShader(type);//功能:设置着色器源代码.const char* src = source.c_str();//功能:替换着色器对象中的源代码。将该id的指定着色器的源代码设置为src指针指向的字符串glShaderSource(id, 1, &src, nullptr);//功能:编译着色器对象的源代码glCompileShader(id);//设置返回着色器的对象IDint result;//功能:从着色器对象返回一个参数,表示编译是否成功。glGetShaderiv(id, GL_COMPILE_STATUS, &result);//如果编译失败,则输出错误信息if (result == GL_FALSE){int length;//功能:获取编译错误信息的长度glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);//分配内存,用于存储编译错误信息char* message = (char*)alloca(length*sizeof(char));//功能:获取编译错误信息glGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile shader!" << (type == GL_VERTEX_SHADER? "Vertex" : "Fragment") << "shader!" << std::endl;std::cout << message << std::endl;//删除着色器对象glDeleteShader(id);return 0;}//TODO:错误处理ingreturn id;
}//功能:创建着色器程序
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{//创建程序对象unsigned int program = glCreateProgram();//编译顶点着色器对象unsigned int vs = CompilesShader(GL_VERTEX_SHADER, vertexShader);//编译片段着色器对象unsigned int fs = CompilesShader(GL_FRAGMENT_SHADER, fragmentShader);//功能:将编译好的着色器对象附加到程序对象中glAttachShader(program, vs);glAttachShader(program, fs);//功能:链接程序对象glLinkProgram(program);//功能:验证着色器程序对象是否可以在当前OpenGL状态中执行。检查着色器程序的完整性和可执行性。glValidateProgram(program);//删除着色器对象,因为它们已经被链接到程序对象中glDeleteShader(vs);glDeleteShader(fs);//返回着色器程序return program;
}int main(void)
{GLFWwindow* window;//初始化glfwif (!glfwInit())return -1;//创建一个窗口模式的窗口并设置OpenGL上下文window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window)//如果窗口创建失败,则终止程序{glfwTerminate();//释放glfw资源return -1;}//设置当前窗口的上下文,之后所有的OpenGL调用都会在这个上下文中进行glfwMakeContextCurrent(window);//初始化GLEWif (glewInit() != GLEW_OK)std::cout << "Error!" << std::endl;//打印OpenGL版本信息std::cout << glGetString(GL_VERSION) << std::endl;//准备数据float position[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f,0.5f,-0.5f,0.5f,};//定义顶点索引缓存,用于标定顶点顺序unsigned int indices[] = {0,1,2,2,3,0};//定义缓冲区对象unsigned int buffer;//功能:生成缓冲区对象,并将数据写入缓冲区glGenBuffers(1, &buffer);//功能:将缓冲区对象绑定到目标glBindBuffer(GL_ARRAY_BUFFER, buffer);//功能:将数据写入缓冲区glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);//功能:配置顶点属性指针glEnableVertexAttribArray(0);//功能:指定顶点属性数组的索引、大小、数据类型、是否归一化、偏移量、数据指针glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);//索引缓冲区对象unsigned int ibo;//功能:生成缓冲区对象,并将数据写入缓冲区glGenBuffers(1, &ibo);//功能:将缓冲区对象绑定到目标.没有绑定为数组缓冲区glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);//功能:将数据写入缓冲区glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);//解析着色器代码文件ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");std::string vertexShader = source.VertexSource;std::string fragmentShader = source.FragmentSource;//创建着色器程序unsigned int shader = CreateShader(vertexShader, fragmentShader);//使用着色器程序glUseProgram(shader);//渲染循环,直到窗口被关闭while (!glfwWindowShouldClose(window)){//清除颜色缓冲区glClear(GL_COLOR_BUFFER_BIT);//功能:绘制三角形GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));//刷新缓冲区并交换窗口glfwSwapBuffers(window);//处理窗口事件,如键盘输入、鼠标移动等glfwPollEvents();}//删除着色器程序//glDeleteProgram(shader);//释放 GLFW 库占用的所有资源。glfwTerminate();return 0;
}

2.2简洁注释代码

#include <GL/glew.h>
#include <GLFW/glfw3.h>#include<iostream>
#include<fstream>
#include<string>
#include<sstream>//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
#define ASSERT(x) if(!(x)) __debugbreak();
//功能:定义了一个宏 GLCall(x),用于调试 OpenGL 应用程序时检查和处理 OpenGL 错误。
#define GLCall(x) GLClearError();\x;\ASSERT(GLLogError(#x,__FILE__,__LINE__));static void GLClearError()
{//功能:检测OpenGL错误while (glGetError() != GL_NO_ERROR);
}//参数:1.function: 发生错误的函数名
static bool GLLogError(const char* function, const char* file, int line)
{//功能:检测OpenGL错误,输出错误信息//while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。while (GLenum error = glGetError()){std::cout << "[OpenGL Error] (" << error << "): " << function << " " << file << ":" << line << std::endl;return false;}return true;
}//功能:定义ShaderProgramSource结构体,用于存储着色器代码
struct ShaderProgramSource
{std::string VertexSource;std::string FragmentSource;
};//功能:解析着色器代码文件。
static ShaderProgramSource ParseShader(const std::string& filepath)
{//功能:打开文件流std::ifstream stream(filepath);//定义着色器类型enum  class ShaderType{NONE=-1,VERTEX=0,FRAGMENT=1};//该变量用于存储着色器代码std::string line;//该变量用于存储着色器类型std::stringstream ss[2];//该变量是当前着色器类型ShaderType type = ShaderType::NONE;//功能:读取文件中的每一行内容,直到文件结束while (getline(stream, line)){//如果当前行包含#shader,则说明接下来是着色器代码if (line.find("#shader") != std::string::npos){//如果当前行包含vertex,则说明接下来是顶点着色器代码if (line.find("vertex") != std::string::npos){type = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos){type = ShaderType::FRAGMENT;}}else{//否则,将当前行添加到对应着色器代码的stringstream中ss[(int)type] << line << '\n';}}//返回ShaderProgramSource结构体return { ss[0].str(), ss[1].str() };
}//功能:编译着色器代码
static unsigned int CompilesShader(unsigned int type, const std::string& source)
{//功能:创建着色器对象unsigned int id = glCreateShader(type);//功能:设置着色器源代码.const char* src = source.c_str();//功能:替换着色器对象中的源代码。将该id的指定着色器的源代码设置为src指针指向的字符串glShaderSource(id, 1, &src, nullptr);//功能:编译着色器对象的源代码glCompileShader(id);//设置返回着色器的对象IDint result;//功能:从着色器对象返回一个参数,表示编译是否成功。glGetShaderiv(id, GL_COMPILE_STATUS, &result);//如果编译失败,则输出错误信息if (result == GL_FALSE){int length;//功能:获取编译错误信息的长度glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);//分配内存,用于存储编译错误信息char* message = (char*)alloca(length*sizeof(char));//功能:获取编译错误信息glGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile shader!" << (type == GL_VERTEX_SHADER? "Vertex" : "Fragment") << "shader!" << std::endl;std::cout << message << std::endl;//删除着色器对象glDeleteShader(id);return 0;}//TODO:错误处理ingreturn id;
}//功能:创建着色器程序
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{//创建程序对象unsigned int program = glCreateProgram();//编译顶点着色器对象unsigned int vs = CompilesShader(GL_VERTEX_SHADER, vertexShader);//编译片段着色器对象unsigned int fs = CompilesShader(GL_FRAGMENT_SHADER, fragmentShader);//功能:将编译好的着色器对象附加到程序对象中glAttachShader(program, vs);glAttachShader(program, fs);//功能:链接程序对象glLinkProgram(program);//功能:验证着色器程序对象是否可以在当前OpenGL状态中执行。检查着色器程序的完整性和可执行性。glValidateProgram(program);//删除着色器对象,因为它们已经被链接到程序对象中glDeleteShader(vs);glDeleteShader(fs);//返回着色器程序return program;
}int main(void)
{GLFWwindow* window;//初始化glfwif (!glfwInit())return -1;//创建一个窗口模式的窗口并设置OpenGL上下文window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window)//如果窗口创建失败,则终止程序{glfwTerminate();//释放glfw资源return -1;}//设置当前窗口的上下文,之后所有的OpenGL调用都会在这个上下文中进行glfwMakeContextCurrent(window);//初始化GLEWif (glewInit() != GLEW_OK)std::cout << "Error!" << std::endl;//打印OpenGL版本信息std::cout << glGetString(GL_VERSION) << std::endl;//准备数据float position[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f,0.5f,-0.5f,0.5f,};//定义顶点索引缓存,用于标定顶点顺序unsigned int indices[] = {0,1,2,2,3,0};//定义缓冲区对象unsigned int buffer;//功能:生成缓冲区对象,并将数据写入缓冲区glGenBuffers(1, &buffer);//功能:将缓冲区对象绑定到目标glBindBuffer(GL_ARRAY_BUFFER, buffer);//功能:将数据写入缓冲区glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);//功能:配置顶点属性指针glEnableVertexAttribArray(0);//功能:指定顶点属性数组的索引、大小、数据类型、是否归一化、偏移量、数据指针glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);//索引缓冲区对象unsigned int ibo;//功能:生成缓冲区对象,并将数据写入缓冲区glGenBuffers(1, &ibo);//功能:将缓冲区对象绑定到目标.没有绑定为数组缓冲区glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);//功能:将数据写入缓冲区glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);//解析着色器代码文件ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");std::string vertexShader = source.VertexSource;std::string fragmentShader = source.FragmentSource;//创建着色器程序unsigned int shader = CreateShader(vertexShader, fragmentShader);//使用着色器程序glUseProgram(shader);//渲染循环,直到窗口被关闭while (!glfwWindowShouldClose(window)){//清除颜色缓冲区glClear(GL_COLOR_BUFFER_BIT);//功能:绘制三角形GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));//刷新缓冲区并交换窗口glfwSwapBuffers(window);//处理窗口事件,如键盘输入、鼠标移动等glfwPollEvents();}//删除着色器程序//glDeleteProgram(shader);//释放 GLFW 库占用的所有资源。glfwTerminate();return 0;
}

2.3运行结果

相关文章:

  • WPF【11_7】WPF实战-重构与美化(ViewModel的嵌套与分解、海量数据不要Join)
  • 备战省赛—全国青少年信息素养大赛-图形化编程-省赛-每日一练-计算台阶
  • Frequent values/gcd区间
  • 【机器学习基础】机器学习入门核心算法:逻辑回归(Decision Tree)
  • eda学习前传又名电赛Day01
  • PIO 也有并发喔,巧用SIDE-SET
  • OpenGL Chan视频学习-8 How I Deal with Shaders in OpenGL
  • 【Doris入门】Doris初识:分布式分析型数据库的核心价值与架构解析
  • 【论文精读】2024 arXiv --VEnhancer现实世界视频超分辨率(RealWorld VSR)
  • Wan2.1 图生视频模型内部协作流程
  • 为(FramePack)的视频生成添加首尾帧功能
  • 多个vue2工程共享node_modules
  • taro + vue3 实现小程序sse长连接实时对话
  • Python的分布式网络爬虫系统实现
  • AI in Game,大模型能力与实时音视频技术融合,交出AI应用新答卷
  • ssh 测试 是否可以连通docker 容器
  • 【Sqoop基础】Sqoop生态集成:与HDFS、Hive、HBase等组件的协同关系深度解析
  • 新电脑配置五 jdk8,maven,idea,vscode
  • IT 运维老手和新手的区别:从手动运维到一体化 IT 运维系统的跨越
  • 第二十二章:数据治理之数据价值:数据价值知多少
  • 商家做网站的优点/nba最新交易
  • 删除wordpress版权信息/搜索引擎优化需要多少钱
  • 做创业项目的网站/百度应用商店官网
  • 一 网站开发背景/策划公司是做什么的
  • 哪个网站教做西餐/百度指数下载
  • 利用网站宣传 两学一做/网站建设营销推广