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

什么是着色器 Shader

在这里插入图片描述

本人就是图形学结课了,对 OpenGL着色器还有很多疑问嘿嘿

文章目录

  • 为什么要有着色器
  • vshader
  • fshader

本文围绕 vshader 和 fshader 代码示例讲解。

(着色器代码取自本人简单OpenGL项目 https://github.com/DBWGLX/-OpenGL-3D-Lighting-and-Shadow-Modeling)

为什么要有着色器

着色器代码其实是给 GPU 跑的。

我们玩 3A 大作,每时每刻画面中大量角色物体可能都在交互变化,并且会因光源而表现不同(高光,明暗)。
时时刻刻计算这么多,CPU 几个核,再加MIPS(每秒百万指令)指令集也根本跑不完。

但是这些计算其实都是简单矩阵间计算,那我们可以换更多的简单核来跑,既不需要太复杂又足够计算 —— GPU

GPU 擅长做 大规模的矩阵运算


  • vshader 负责把物体规划好【模型空间 -> 世界空间 -> 相机空间 -> 屏幕空间】
  • fshader 结合光照和纹理,对显示器屏幕上的每个“像素点”计算最终颜色【给每个像素上色】

vshader

Vertex Shader 顶点着色器

【顶点着色器】是图形管线的第一阶段,作用是接收每个顶点的信息(如位置、颜色、法线、投影等),对其进行计算,并输出一些信息供后续的【片元着色器】使用。

比如三位建模的物体移动,模型的所有点、面都要移动,我们提供坐标给GPU让他来算位置。(当然还有透视投影,跟多关于 “投影和相机” 可以阅读以了解:https://blog.csdn.net/JK01WYX/article/details/143242785)

下面代码的 in 变量就是我们 cpp 程序提前绑定的,out 就是给 fshader 的,uniform(统一的)就是绑定好在vshader里 大家都用来计算的(相机坐标系,投影方式等)。

#version 330 core // GLSL(OpenGL Shading Language),版本是 #version 330 core,即 OpenGL 3.3 核心版本。// 顶点着色器
in vec3 vPosition;	// 顶点位置,3D坐标
in vec3 vColor;		// 颜色值(如 RGB)
in vec3 vNormal;	// 法向量,通常用于光照计算
in vec2 vTexture;	// 纹理坐标,通常范围在 [0, 1]// 传给片元着色器的变量
out vec3 position;
out vec3 normal;
out vec3 color;
out vec2 texCoord;	// texture coordinate 纹理坐标// 模型变换矩阵、相机观察矩阵、投影矩阵
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main() 
{// 把顶点位置乘以模型矩阵,变换到世界空间。 // vec4() 是把 3D 坐标变成 4D 齐次坐标。vec4 v1 = model * vec4(vPosition, 1.0);  // 由于model矩阵有可能为阴影矩阵,为了得到正确位置,我们需要做一次透视除法vec4 v2 = vec4(v1.xyz / v1.w, 1.0);// 先应用视图矩阵(把世界空间转换到相机空间),再应用投影矩阵vec4 v3 = projection* view * v2;// 最终设置到 gl_Position,这是 GPU 用来决定顶点【最终在屏幕上位置】的变量gl_Position = v3;// 最后:传递变量给片元着色器position = vec3(v2.xyz);normal = vec3( (model *  vec4(vNormal, 0.0)).xyz ); // 法线向量是方向,不需要平移,所以 w = 0.0。color = vColor;texCoord = vTexture;}

fshader

我当时加载了三角面片和四角面片的模型,自己去做了适配。

甚至后面在 B站的模之屋 下载了多网格并各自带纹理的模型,去 Blender 转成 .obj 去加载 (当然失败,纹理这块)

其实也完全可以写多个着色器的。

#version 330 core// 光源结构体
struct Light{vec4 ambient;	// 环境光颜色(整个空间的背景亮度)vec4 diffuse;	// 漫反射颜色(光打到表面后均匀反射)vec4 specular;	// 镜面高光颜色(发亮的高光)vec3 position;	// 光源的位置// 控制光照距离衰减的三个参数float constant; // 常数项float linear;	// 一次项float quadratic;// 二次项
};// 材质属性结构体
struct Material{// 物体本身对应三种反射光的颜色属性vec4 ambient;vec4 diffuse;vec4 specular;// 镜面反射的“锐利程度”(越大越亮 越小越模糊)float shininess;
};// In
in vec3 position;
in vec3 normal;
in vec3 color;
in vec2 texCoord;uniform vec3 eye_position;	// 相机坐标
uniform Light light;		// 光源uniform Material material;	// 物体材质
uniform int isShadow;		// 是否为阴影
uniform int hasTextureMap;	// 是否使用纹理
uniform sampler2D texture;  // 纹理采样器// 这是片元最终的颜色结果,会输出到屏幕上。
out vec4 fColor;void main()
{if (isShadow == 1) {	// 如果当前是画阴影贴图,就直接涂成黑色,不用算光照。fColor = vec4(0.0, 0.0, 0.0, 1.0);}else {// 将顶点坐标、光源坐标和法向量转换到相机坐标系vec3 norm = (vec4(normal, 0.0)).xyz;vec3 N = normalize(norm);						// 法向量vec3 L = normalize(light.position - position);	// 光源方向//vec3 L = normalize(light.position);			// 平行光vec3 V = normalize(eye_position - position);	// 视角方向vec3 R = reflect(-L, N);						// 反射方向vec3 H = normalize(L + V);						// 半角向量(可选)vec4 I_a = light.ambient * material.ambient;	// 环境光 = 光源的环境光 × 材质的环境反射能力// @TODO: Task2 计算系数和漫反射分量I_d			// 漫反射 = max(光线方向·法线方向, 0) × 光的亮度 × 材质漫反射颜色float diffuse_dot = max(dot(L, N), 0.0);vec4 I_d = diffuse_dot * light.diffuse * material.diffuse;// @TODO: Task2 计算系数和镜面反射分量I_s			// 镜面高光 = 视角方向·反射方向的夹角float specular_dot_pow = pow(max(dot(V, R), 0.0), material.shininess);// shininess 越大,高光越小越亮,表示光滑表面// float specular_dot_pow = pow( clamp(dot(V, R), 0.0, 1.0), material.shininess );vec4 I_s = specular_dot_pow * light.specular * material.specular;// 光在背面就不该出现高光效果if (dot(L, N) < 0.0) {I_s = vec4(0.0, 0.0, 0.0, 1.0);}fColor = vec4(color, 1.0);if (hasTextureMap==1) {//纹理基础颜色vec4 textureColor = texture2D(texture, texCoord);// 合并三个分量的颜色,修正透明度fColor = textureColor;}// 总结三种光照并输出颜色 : 最终颜色 = 材质色 × (环境光 + 漫反射 + 镜面反射)fColor *= I_a + I_d + I_s;fColor.a = 1.0; // 保证不透明}
}

这段 shader 是个非常典型的 Phong 光照模型 + 纹理融合 示例,非常值得你自己调试、注释、修改看看不同效果。

相关文章:

  • 正则表达式与文本处理的艺术
  • WPS多级标题编号以及样式控制
  • k6学习k6学习k6学习k6学习k6学习k6学习
  • 【Android】从Choreographer到UI渲染(二)
  • Linux虚拟文件系统(1)
  • Spark,数据提取和保存
  • 数组随机重排与维度转换算法
  • 深入解析Python中的Vector2d类:从基础实现到特殊方法的应用
  • ngx_http_random_index_module 模块概述
  • LoadBarWorks:一款赛博风加载动画生成器的构建旅程
  • linux下的 xargs命令使用详解
  • 墨水屏显示模拟器程序解读
  • jqGrid冻结列错行问题,将冻结表格(悬浮表格)与 正常表格进行高度同步
  • HarmonyOS AVPlayer 音频播放器
  • MyBatis 核心技术详解:从连接池到多表查询
  • 聊天室项目总结
  • 主成分分析的应用之sklearn.decomposition模块的PCA函数
  • [Android] 青木扫描全能文档3.0,支持自动扫描功能
  • 从0开始学linux韦东山教程第四章问题小结(1)
  • 单片机设计_停车场车位管理系统(AT89C52、LCD1602)
  • 三件珍贵标本开箱!中国恐龙大展5月26日在沪开幕,明星标本汇聚一堂
  • 全中国最好的十个博物馆展陈选出来了!
  • 看展 | 黄永玉新作展,感受赤子般的生命力
  • 上海天文馆走进徐家汇书院,XR沉浸式天文科普体验再推力作
  • 著名文博专家吴远明因交通事故离世,享年75岁
  • 乌克兰官员与法德英美四国官员举行会谈