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

【OpenGL】shader 着色器

参考资料:https://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-2-the-first-triangle/

一、着色器

在openGL固定渲染管线的版本,不存在着色器这一概念,或者说,着色器是不可以编程的。在可编程管线中,着色器分为顶点着色器和片段着色器两个阶段,分别是用来对图元的顶点和片源进行操作。

1.1 编译着色器

  • 在最简配置下,您得有两个着色器:一个叫顶点着色器(vertex shader),它将作用于每个顶点上;另一个叫片段着色器(fragment shader),它将作用于每一个采样点。我们采用4倍抗锯齿,因此每个像素有四个采样点。

  • 着色器编程使用GLSL(GL Shading Language),属于OpenGL的一部分。与C、Java不同,GLSL必须在运行时编译,这意味着每次启动程序时,所有的着色器将重新编译。

  • 这两个着色器通常单独存放在文件里。本例中有shader.vertshader.frag两个着色器。扩展名无关紧要,也可以是.txt或者.glsl。

  • 以下是加载着色器的代码。没必要完全理解,因为在程序中这些操作一般只需执行一次,结合注释能看懂就够了

编译着色器的函数如下,主要功能就是编译顶点着色器和片段着色器,创建着色器程序,然后链接编译好的这两个着色器,最后返回生成好的着色器程序句柄。

void MyGLWidget::loadShader(std::string const& vertex_shader_path,std::string const& fragment_shader_path) {  //创建顶点着色器  vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);  //读取shader.vert  std::string vertex_shader_code;  std::ifstream vertex_shader_stream(vertex_shader_path,std::ios::in);  if(vertex_shader_stream.is_open()){  std::stringstream ss;  ss << vertex_shader_stream.rdbuf();  vertex_shader_code = ss.str();  vertex_shader_stream.close();  }  else{  std::cout << "current path = " << std::filesystem::current_path() << std::endl;  if (!std::filesystem::exists(vertex_shader_path)) {  std::cerr << "File not found: " << vertex_shader_path << std::endl;  }  std::cerr << "read vertex_shader fail!" << std::endl;  return;  }  //加载vertex着色器程序代码  const char* vertex_shader_code_pointer = vertex_shader_code.c_str();  glShaderSource(vertex_shader_id,1,&vertex_shader_code_pointer,nullptr);  //编译vertex shader  glCompileShader(vertex_shader_id);  //查看vertex编译结果  GLint Result = GL_FALSE;  int infoLogLength;  glGetShaderiv(vertex_shader_id,GL_COMPILE_STATUS,&Result);  glGetShaderiv(vertex_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char> vertex_shader_error_message(infoLogLength+1);  glGetShaderInfoLog(vertex_shader_id, infoLogLength, nullptr, &vertex_shader_error_message[0]);  std::cout << "vertex shader:" << &vertex_shader_error_message[0] << std::endl;  }  //创建片段着色器  fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);  //读取shader.frag  std::string fragment_shader_code;  std::ifstream fragment_shader_stream(fragment_shader_path,std::ios::in);  if(fragment_shader_stream.is_open()){  std::stringstream ss;  ss << fragment_shader_stream.rdbuf();  fragment_shader_code = ss.str();  fragment_shader_stream.close();  }  else{  std::cout << "current path = " << std::filesystem::current_path() << std::endl;  if (!std::filesystem::exists(vertex_shader_path)) {  std::cerr << "File not found: " << vertex_shader_path << std::endl;  }  std::cerr << "read fragment_shader fail!" << std::endl;  }  //加载fragment着色器程序代码  const char* fragment_shader_code_pointer = fragment_shader_code.c_str();  glShaderSource(fragment_shader_id,1,&fragment_shader_code_pointer,nullptr);  //编译fragment shader  glCompileShader(fragment_shader_id);  //查看fragment编译结果  glGetShaderiv(fragment_shader_id,GL_COMPILE_STATUS,&Result);  glGetShaderiv(fragment_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char> fragment_shader_error_message(infoLogLength+1);  glGetShaderInfoLog(fragment_shader_id, infoLogLength, nullptr, &fragment_shader_error_message[0]);  std::cout << "fragment shader compile error:" << &fragment_shader_error_message[0] << std::endl;  }  //创建着色器程序  shader_program_id = glCreateProgram();  //附加着色器到程序  glAttachShader(shader_program_id,vertex_shader_id);  glAttachShader(shader_program_id,fragment_shader_id);  //链接shader  glLinkProgram(shader_program_id);  //检查程序链接结果  glGetProgramiv(shader_program_id,GL_LINK_STATUS,&Result);  glGetProgramiv(shader_program_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char>program_error_message(infoLogLength + 1);  glGetProgramInfoLog(shader_program_id,infoLogLength,nullptr,&program_error_message[0]);  std::cout << "program link:" << &program_error_message[0] << std::endl;  }  //删除着色器编译结果  glDeleteShader(vertex_shader_id);  glDeleteShader(fragment_shader_id);  
}

链接好之后,之前编译的结果就可以删除了,不再需要了

//删除着色器编译结果  
glDeleteShader(vertex_shader_id);  
glDeleteShader(fragment_shader_id);  

1.2 顶点着色器

先写顶点着色器。 第一行告诉编译器我们将用OpenGL 3语法。

#version 330 core

第二行声明输入数据:

layout(location = 0) in vec3 vertexPosition_modelspace;

下面详细解释这一行:

  • 在GLSL中“vec3”代表一个三维向量。类似但不等同于之前声明三角形的glm::vec3。最重要的是,如果我们在C++中使用三维向量,那么在GLSL中也要相应地使用三维向量。
  • layout(location = 0)“指向存储vertexPosition_modelspace属性(attribute)的缓冲。每个顶点有多种属性:位置,一种或多种颜色,一个或多个纹理坐标等等。OpenGL并不清楚什么是颜色,它只能识别vec3这样的数据类型。因此我们必须将glvertexAttribPointer函数的第一个参数值赋给layout,以此告知OpenGL每个缓冲对应的是哪种属性数据。第二个参数“0”并不重要,也可以换成12(但是不能超过glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &v)),关键是C++和GLSL两边数值必须保持一致。
  • vertexPosition_modelspace”这个变量名可以任取,其中保存的是顶点位置,顶点着色器每次运行时都会用到。
  • in”表明是这是输入数据。不久我们将会看到“out”关键字。

每个顶点都会调用main函数(和C语言一样):

void main(){

这里的main函数只是简单地将缓冲里的值作为顶点位置。因此如果位置是(1,1),那么三角形有一个顶点位于屏幕的右上角。 在下一课中我们将看到怎样对输入位置做一些更有趣的计算。

gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
}

gl_Position是仅有的几个内置变量之一:您必须对其赋值。其他操作都是可选的,我们将在第四课中看到究竟有哪些“其他操作”。

1.3 片段着色器

这就是我们的第一个片段着色器,它仅仅简单将每个片段的颜色设为红色。(记住,我们采用了4倍抗锯齿,因此每个像素有4个片段)

#version 330 core
out vec3 color;void main(){
color = vec3(1,0,0);
}

vec3(1,0,0)代表红色。因为在计算机屏幕上,颜色由红、绿、蓝三元组表示。因此(1,0,0)代表纯红色,无绿、蓝分量。

二、使用着色器

首先,在initializeGL函数中,加载我们的着色器程序,并且保存返回的句柄

//加载着色器  
loadShader("/Users/liuhang/CLionProjects/opengl-learning/opengl1-2-shader/shader/shader.vert",  "/Users/liuhang/CLionProjects/opengl-learning/opengl1-2-shader/shader/shader.frag");

然后,在paintGL中使用保存的着色器程序

glUseProgram(shader_program_id);

其余代码和前面章节保持一致,运行效果如下:

在这里插入图片描述

二、完整代码

my_glwidget.h

//  
// Created by liuhang on 2025/9/16.  
//  #ifndef OPENGL_LEARNING_MY_GLWIDGET_H  
#define OPENGL_LEARNING_MY_GLWIDGET_H  #include<QOpenGLWidget>  
#include<QOpenGLFunctions>  class MyGLWidget : public QOpenGLWidget,protected QOpenGLFunctions  
{  
public:  explicit MyGLWidget(QWidget* parent = nullptr);  ~MyGLWidget() override;  protected:  void initializeGL() override;  void paintGL() override;  void resizeGL(int w,int h) override;  private:  void loadShader(std::string const& vertex_shader_path,std::string const& fragment_shader_path);  private:  GLuint vertex_array_id;  GLuint vertex_buffer_id;  GLuint vertex_shader_id;  GLuint fragment_shader_id;  GLuint shader_program_id;  
};  #endif //OPENGL_LEARNING_MY_GLWIDGET_H

my_glwidget.cpp

//  
// Created by liuhang on 2025/9/16.  
//  #include "my_glwidget.h"  
#include<string>  
#include<fstream>  
#include<sstream>  
#include<iostream>  
#include<filesystem>  static const float vertex_buffer_data[] ={  -1.0f, -1.0f, 0.0f,  1.0f, -1.0f, 0.0f,  0.0f,  1.0f, 0.0f  
};  MyGLWidget::MyGLWidget(QWidget *parent): QOpenGLWidget(parent)  
{  }  MyGLWidget::~MyGLWidget() {  makeCurrent();  glDeleteVertexArrays(1,&vertex_array_id);  glDeleteBuffers(1,&vertex_buffer_id);  doneCurrent();  
}  void MyGLWidget::initializeGL() {  initializeOpenGLFunctions();  glClearColor(0.2f,0.3f,0.3f,1.0f);  glGenVertexArrays(1,&vertex_array_id);  glBindVertexArray(vertex_array_id);  glGenBuffers(1,&vertex_buffer_id);  glBindBuffer(GL_ARRAY_BUFFER,vertex_buffer_id);  glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_buffer_data),vertex_buffer_data,GL_STATIC_DRAW);  //启用layout = 0为当前绑定的VAO  glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,nullptr);  glEnableVertexAttribArray(0);  //加载着色器  loadShader("/Users/liuhang/CLionProjects/opengl-learning/opengl1-2-shader/shader/shader.vert",  "/Users/liuhang/CLionProjects/opengl-learning/opengl1-2-shader/shader/shader.frag");  //解绑VAO  glBindVertexArray(0);  
}  void MyGLWidget::paintGL() {  glClear(GL_COLOR_BUFFER_BIT);  glBindVertexArray(vertex_array_id);  //使用着色器程序  glUseProgram(shader_program_id);  glDrawArrays(GL_TRIANGLES,0,3);  
}  void MyGLWidget::resizeGL(int w, int h) {  glViewport(0, 0, w, h);  
}  void MyGLWidget::loadShader(std::string const& vertex_shader_path,std::string const& fragment_shader_path) {  //创建顶点着色器  vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);  //读取shader.vert  std::string vertex_shader_code;  std::ifstream vertex_shader_stream(vertex_shader_path,std::ios::in);  if(vertex_shader_stream.is_open()){  std::stringstream ss;  ss << vertex_shader_stream.rdbuf();  vertex_shader_code = ss.str();  vertex_shader_stream.close();  }  else{  std::cout << "current path = " << std::filesystem::current_path() << std::endl;  if (!std::filesystem::exists(vertex_shader_path)) {  std::cerr << "File not found: " << vertex_shader_path << std::endl;  }  std::cerr << "read vertex_shader fail!" << std::endl;  return;  }  //加载vertex着色器程序代码  const char* vertex_shader_code_pointer = vertex_shader_code.c_str();  glShaderSource(vertex_shader_id,1,&vertex_shader_code_pointer,nullptr);  //编译vertex shader  glCompileShader(vertex_shader_id);  //查看vertex编译结果  GLint Result = GL_FALSE;  int infoLogLength;  glGetShaderiv(vertex_shader_id,GL_COMPILE_STATUS,&Result);  glGetShaderiv(vertex_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char> vertex_shader_error_message(infoLogLength+1);  glGetShaderInfoLog(vertex_shader_id, infoLogLength, nullptr, &vertex_shader_error_message[0]);  std::cout << "vertex shader:" << &vertex_shader_error_message[0] << std::endl;  }  //创建片段着色器  fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);  //读取shader.frag  std::string fragment_shader_code;  std::ifstream fragment_shader_stream(fragment_shader_path,std::ios::in);  if(fragment_shader_stream.is_open()){  std::stringstream ss;  ss << fragment_shader_stream.rdbuf();  fragment_shader_code = ss.str();  fragment_shader_stream.close();  }  else{  std::cout << "current path = " << std::filesystem::current_path() << std::endl;  if (!std::filesystem::exists(vertex_shader_path)) {  std::cerr << "File not found: " << vertex_shader_path << std::endl;  }  std::cerr << "read fragment_shader fail!" << std::endl;  }  //加载fragment着色器程序代码  const char* fragment_shader_code_pointer = fragment_shader_code.c_str();  glShaderSource(fragment_shader_id,1,&fragment_shader_code_pointer,nullptr);  //编译fragment shader  glCompileShader(fragment_shader_id);  //查看fragment编译结果  glGetShaderiv(fragment_shader_id,GL_COMPILE_STATUS,&Result);  glGetShaderiv(fragment_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char> fragment_shader_error_message(infoLogLength+1);  glGetShaderInfoLog(fragment_shader_id, infoLogLength, nullptr, &fragment_shader_error_message[0]);  std::cout << "fragment shader compile error:" << &fragment_shader_error_message[0] << std::endl;  }  //创建着色器程序  shader_program_id = glCreateProgram();  //附加着色器到程序  glAttachShader(shader_program_id,vertex_shader_id);  glAttachShader(shader_program_id,fragment_shader_id);  //链接shader  glLinkProgram(shader_program_id);  //检查程序链接结果  glGetProgramiv(shader_program_id,GL_LINK_STATUS,&Result);  glGetProgramiv(shader_program_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char>program_error_message(infoLogLength + 1);  glGetProgramInfoLog(shader_program_id,infoLogLength,nullptr,&program_error_message[0]);  std::cout << "program link:" << &program_error_message[0] << std::endl;  }  //删除着色器编译结果  glDeleteShader(vertex_shader_id);  glDeleteShader(fragment_shader_id);  
}

shader.vert

#version 330 core  layout(location = 0) in vec3 vertexPosion_modelspace;  void main(){  gl_Position = vec4(vertexPosion_modelspace,1.0);  
}

shader.frag

#version 330 core  out vec3 fragment_color;  void main(){  fragment_color = vec3(1.0,0.0,0.0);  
}
http://www.dtcms.com/a/393401.html

相关文章:

  • 给AI装上“眼睛”:Schema标记和技术性GEO实战部署
  • 中超-克雷桑破门 齐鲁德比泰山2-2遭海牛读秒绝平!
  • gitflow在公司的全流程
  • 如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘grpc’ 问题
  • C语言第17讲
  • 人机协同开发中的“深水炸弹”——指令上下文混淆
  • 朴素贝叶斯算法详解:原理、应用与实践
  • 强化学习的数学原理-02章 贝尔曼公式
  • C++:入门基础(2)
  • 数据架构章节考试考点及关系梳理
  • 用TRAE编程助手编写一个浏览器插件
  • 赋能工业未来:向成电子XC3576H工控主板多领域应用前景
  • Multi-Agent多智能体系统(三)
  • 【语法进阶】高级用法、贪婪与非贪婪
  • 15天见效的SEO优化方案
  • C语言基础【20】:指针7
  • IC 数字逻辑设计中的硬件算法 01 记
  • 《棒球运动联盟》国家级运动健将标准·棒球1号位
  • AAC 详解
  • 蚂蚁集团DIVER登顶BRIGHT榜首,开源多阶段推理检索范式
  • 2013/12 JLPT听力原文 问题四
  • 挑战与应对:轻量化 AI 算法的成长烦恼
  • FPGA基础 -- CDC(Clock Domain Crossing)实战教程
  • 低碳经济:碳汇——从生态固碳到金融资产的价值转化
  • QGC 通信模块架构梳理
  • Application接口拓展功能(三)
  • 【Python】错误和异常
  • 【状态机实现】初识——基于状态机实现的流程编排和Activiti、Camunda、Flowable等工作流的区别
  • SpringBoot自动配置核心原理
  • Python 中的 Builder 模式实践 —— 以 UserProfileBuilder 为例