WebGL 的 Shader 与 GLSL 全面解析:顶点着色器、片段着色器与着色器语言实战指南
图形学【免费下载链接】webgl-fundamentalsWebGL lessons that start with the basics项目地址https://gitcode.com/gh_mirrors/we/webgl-fundamentals点击查看免费下载导读本文是 webgl-fundamentals 开源课程系列中讲解「着色器Shader与 GLSL」的核心篇章对应仓库中的 日文版讲义 与 英文版原文。文中将系统梳理 WebGL 渲染管线中两大着色器——顶点着色器Vertex Shader与片段着色器Fragment Shader的职责与固定形态逐一拆解它们获取数据的三种途径attribute、uniform、texture/varying并深入介绍 GLSL 语言特有的向量/矩阵类型、分量选择器swizzle、强类型约束与内置函数体系。读完本文你将具备独立阅读和编写 GLSL 着色器、正确配置缓冲区与 uniform、理解 varying 插值原理的完整实战能力为后续 2D 变换、图像处理与 3D 图形打下坚实基础。本系列课程建议按顺序阅读WebGL 基础 → WebGL 的工作原理 → 本文。若尚未了解 WebGL 内部的运行机制建议先阅读「WebGL 的工作原理」一章。着色器与着色器程序Shader Program概述正如「WebGL 的工作原理」中所阐述的WebGL 每次绘制任何图形时都需要两个着色器顶点着色器Vertex Shader片段着色器Fragment Shader每一个着色器本质上都是一个「函数」。顶点着色器与片段着色器会被链接到一起构成一个「着色器程序Shader Program」通常简称为「程序Program」。一个典型的 WebGL 应用会拥有多个着色器程序分别用于不同的绘制效果。在仓库的 webgl-fundamentals.html 中可以看到两者的创建与链接过程// create GLSL shaders, upload the GLSL source, compile the shaders var vertexShader createShader(gl, gl.VERTEX_SHADER, vertexShaderSource); var fragmentShader createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); // Link the two shaders into a program var program createProgram(gl, vertexShader, fragmentShader);其中createShader与createProgram是仓库中提供的基础封装函数见 webgl-fundamentals.html分别完成gl.createShader→gl.shaderSource→gl.compileShader的编译流程以及gl.createProgram→gl.attachShader→gl.linkProgram的链接流程并会通过gl.getShaderParameter/gl.getProgramParameter检查编译与链接状态。顶点着色器Vertex Shader职责与固定形态顶点着色器的职责是生成裁剪空间坐标clip space coordinates。它必须采用如下固定形态void main() { // 进行某些计算生成「裁剪空间坐标」的值。 // 将计算结果赋给 gl_Position。 gl_Position doMathToMakeClipspaceCoordinates; }顶点着色器对每一个顶点调用一次。每次被调用时都必须将某个裁剪空间坐标值写入特殊的全局变量gl_Position。顶点着色器需要输入数据它获取数据的方式共有三种attribute属性——从「缓冲区buffer」中取出的数据uniform——在一次绘制调用draw call期间对所有顶点都保持相同的值texture纹理——从「像素/纹素texel」中读取的数据attribute属性从缓冲区读取数据最常用的方式是通过**缓冲区buffer**与attribute属性来传递数据这一点在「WebGL 的工作原理」中已有说明。首先创建缓冲区var buf gl.createBuffer();向缓冲区中写入数据gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, someData, gl.STATIC_DRAW);然后针对已创建的着色器程序在初始化阶段查询其 attribute 的位置locationvar positionLoc gl.getAttribLocation(someShaderProgram, a_position);在渲染阶段则需要告知 WebGL 如何从缓冲区中取出数据并送入该 attribute// 针对指定 attribute启用从缓冲区读取数据 gl.enableVertexAttribArray(positionLoc); var numComponents 3; // (x, y, z) 共 3 个数据 var type gl.FLOAT; // 32 位浮点数 var normalize false; // 不进行自动归一化处理 var offset 0; // 从缓冲区开头读取 var stride 0; // 每个顶点数据的字节数 // 0 按「数据类型与数据个数」采用相应的 stride gl.vertexAttribPointer(positionLoc, numComponents, type, false, stride, offset);其中stride用于描述「从当前顶点数据到下一个顶点数据需要跳过多少字节」在数据紧密排列时设为0即可WebGL 会自动按照type与numComponents计算正确的步长。回顾「WebGL 基础」中的示例顶点着色器可以直接把从缓冲区读取的数据原样输出不做任何计算attribute vec4 a_position; void main() { gl_Position a_position; }这个例子之所以可行是因为在把数据放入缓冲区时数据本身已经是裁剪空间坐标因此着色器内无需再做任何数学变换。仓库中的 webgl-fundamentals.html 完整演示了这套流程顶点数据positions三个 2D 裁剪空间点写入缓冲区后通过gl.vertexAttribPointer配置读取规则最终由gl.drawArrays(gl.TRIANGLES, 0, 3)触发绘制。attribute 可用的数据类型包括float、vec2、vec3、vec4、mat2、mat3、mat4。uniform一次绘制调用内保持不变的公共值uniform 是指着色器中在一次绘制调用即一次gl.drawArrays/gl.drawElements调用期间对所有顶点都保持相同值的变量。最简单的例子是在顶点着色器中为坐标增加一个偏移量attribute vec4 a_position; uniform vec4 u_offset; void main() { gl_Position a_position u_offset; }这样就能为每个顶点都加上相同的偏移量从而使整个图形产生平移。使用 uniform 时首先在初始化阶段查询着色器程序中定义的 uniform 位置var offsetLoc gl.getUniformLocation(someProgram, u_offset);然后在绘制前设置 uniform 的值gl.uniform4fv(offsetLoc, [1, 0, 0, 0]); // 向右X 正方向移动半屏uniform 的重要特性使用 uniform 时需要注意uniform 隶属于各自的着色器程序。如果拥有多个着色器程序且它们定义了同名的 uniform这些 uniform 彼此独立各自拥有不同的位置location也各自保存不同的值。调用gl.uniform???时只会设置「当前程序current program」的 uniform而当前程序由最后一次调用gl.useProgram所指定的程序决定。uniform 的完整类型与对应设置函数uniform 可以使用的类型很多设置数据时必须调用与着色器程序中 uniform 定义相匹配的函数gl.uniform1f (floatUniformLoc, v); // float gl.uniform1fv(floatUniformLoc, [v]); // float或 float 数组 gl.uniform2f (vec2UniformLoc, v0, v1); // vec2 gl.uniform2fv(vec2UniformLoc, [v0, v1]); // vec2或 vec2 数组 gl.uniform3f (vec3UniformLoc, v0, v1, v2); // vec3 gl.uniform3fv(vec3UniformLoc, [v0, v1, v2]); // vec3或 vec3 数组 gl.uniform4f (vec4UniformLoc, v0, v1, v2, v4); // vec4 gl.uniform4fv(vec4UniformLoc, [v0, v1, v2, v4]); // vec4或 vec4 数组 gl.uniformMatrix2fv(mat2UniformLoc, false, [ 元素个数为 4 的倍数的数组 ]) // mat2或 mat2 数组 gl.uniformMatrix3fv(mat3UniformLoc, false, [ 元素个数为 9 的倍数的数组 ]) // mat3或 mat3 数组 gl.uniformMatrix4fv(mat4UniformLoc, false, [ 元素个数为 16 的倍数的数组 ]) // mat4或 mat4 数组 gl.uniform1i (intUniformLoc, v); // int gl.uniform1iv(intUniformLoc, [v]); // int或 int 数组 gl.uniform2i (ivec2UniformLoc, v0, v1); // ivec2 gl.uniform2iv(ivec2UniformLoc, [v0, v1]); // ivec2或 ivec2 数组 gl.uniform3i (ivec3UniformLoc, v0, v1, v2); // ivec3 gl.uniform3iv(ivec3UniformLoc, [v0, v1, v2]); // ivec3或 ivec3 数组 gl.uniform4i (ivec4UniformLoc, v0, v1, v2, v4); // ivec4 gl.uniform4iv(ivec4UniformLoc, [v0, v1, v2, v4]); // ivec4或 ivec4 数组 gl.uniform1i (sampler2DUniformLoc, v); // sampler2D纹理 gl.uniform1iv(sampler2DUniformLoc, [v]); // sampler2D或 sampler2D 数组 gl.uniform1i (samplerCubeUniformLoc, v); // samplerCube纹理 gl.uniform1iv(samplerCubeUniformLoc, [v]); // samplerCube或 samplerCube 数组此外还有处理布尔值的bool、bvec2、bvec3、bvec4类型设置它们可以使用gl.uniform?f?或gl.uniform?i?系列函数。uniform 数组的批量设置与逐元素设置若 uniform 被定义为数组可以一次性设置整个数组的值// 着色器侧代码 uniform vec2 u_someVec2[3];// JavaScript 侧代码初始化阶段 var someVec2Loc gl.getUniformLocation(someProgram, u_someVec2); // JavaScript 侧代码渲染阶段 gl.uniform2fv(someVec2Loc, [1, 2, 3, 4, 5, 6]); // 一次性设置 u_someVec2 整个数组也可以逐个元素设置但此时必须分别获取数组每个元素的位置// JavaScript 侧代码初始化阶段 var someVec2Element0Loc gl.getUniformLocation(someProgram, u_someVec2[0]); var someVec2Element1Loc gl.getUniformLocation(someProgram, u_someVec2[1]); var someVec2Element2Loc gl.getUniformLocation(someProgram, u_someVec2[2]); // JavaScript 侧代码渲染阶段 gl.uniform2fv(someVec2Element0Loc, [1, 2]); // 设置 u_someVec2[0] 的值 gl.uniform2fv(someVec2Element1Loc, [3, 4]); // 设置 u_someVec2[1] 的值 gl.uniform2fv(someVec2Element2Loc, [5, 6]); // 设置 u_someVec2[2] 的值结构体structuniform 的字段级位置查询GLSL 还支持结构体语法struct SomeStruct { bool active; vec2 someVec2; }; uniform SomeStruct u_someThing;使用结构体时需要针对结构体的每一个字段分别查询位置var someThingActiveLoc gl.getUniformLocation(someProgram, u_someThing.active); var someThingSomeVec2Loc gl.getUniformLocation(someProgram, u_someThing.someVec2);顶点着色器中使用纹理顶点着色器中读取纹理的方法与片段着色器中使用纹理相同详见后文。片段着色器Fragment Shader职责与固定形态片段着色器的职责是为被光栅化的当前像素决定颜色。它必须采用如下固定形态precision mediump float; void main() { // 进行某些计算决定「像素的颜色」。 // 将计算结果赋给 gl_FragColor。 gl_FragColor doMathToMakeAColor; }片段着色器对每一个像素调用一次。每次被调用时都必须将某种颜色信息写入特殊的全局变量gl_FragColor。注意片段着色器没有默认的精度声明因此开头必须显式写出precision mediump float;mediump是一个不错的默认选择见仓库 webgl-fundamentals.html 中的实际用法。片段着色器同样需要数据获取方式有三种uniform——一次绘制调用期间对所有像素都保持相同的值texture纹理——从「像素/纹素」读取的数据varying——从顶点着色器传来的数据会按需进行插值片段着色器中的 uniform其机制与顶点着色器中的 uniform 完全相同请参照前述章节。片段着色器中使用纹理在着色器中从纹理取值需要声明sampler2D类型的 uniform并使用 GLSL 函数texture2D从中取出值precision mediump float; uniform sampler2D u_texture; void main() { vec2 texcoord vec2(0.5, 0.5); // 获取纹理中央的值 gl_FragColor texture2D(u_texture, texcoord); }从纹理中取出的数据究竟是什么样的取决于众多设置项其细节在「3D 纹理」一章中有深入讲解这里先用一个满足最低需求的示例来说明纹理的完整使用流程。首先创建纹理并设置数据var tex gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); var level 0; var width 2; var height 1; var data new Uint8Array([ 255, 0, 0, 255, // 红色像素 0, 255, 0, 255, // 绿色像素 ]); gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);在初始化阶段查询着色器程序中定义的 uniform 位置var someSamplerLoc gl.getUniformLocation(someProgram, u_texture);在渲染阶段首先将纹理绑定到某一个「纹理单元texture unit」var unit 5; // 指定某一个纹理单元 gl.activeTexture(gl.TEXTURE0 unit); gl.bindTexture(gl.TEXTURE_2D, tex);然后告诉着色器「你绑定的是哪个纹理单元」gl.uniform1i(someSamplerLoc, unit);关于纹理单元的补充说明所有纹理相关的 WebGL API 都作用于「活动纹理单元active texture unit」它相当于一个全局变量指明当前操作作用于哪一个纹理单元。仓库 webgl-2-textures.md 对此有详细解释并用「用 JavaScript 模拟 WebGL 内部实现」的方式展示了gl.activeTexture/gl.bindTexture/gl.texImage2D背后所维护的textureUnits数组与activeTextureUnit索引。注意 uniform 侧使用从 0 开始的整数指定纹理单元如gl.uniform1i(u_image0Location, 0)而gl.activeTexture侧使用gl.TEXTURE0、gl.TEXTURE1等预定义常量好在这些常量被定义为连续的整数值因此可以写成gl.activeTexture(gl.TEXTURE0 ii)这样的循环形式见 webgl-2-textures.md。仓库中的 webgl-3d-textures.html 展示了sampler2Dtexture2D在实际 3D 示例中的标准用法配合从顶点着色器传来的 varying 纹理坐标使用。varying从顶点着色器到片段着色器的数据通道varying 是从顶点着色器向片段着色器传递数据的手段这在「WebGL 的工作原理」中已有介绍。使用 varying 时需要在顶点着色器与片段着色器中成对声明同名的 varying。顶点着色器针对每个顶点调用一次并在其中设置「该顶点的 varying 值」。当 WebGL 绘制像素时会对顶点之间的区域进行插值interpolation——即填补顶点与顶点之间的空白——同时对 varying 的值也进行插值。片段着色器被调用时拿到的就是插值后的 varying 值。顶点着色器attribute vec4 a_position; uniform vec4 u_offset; varying vec4 v_positionWithOffset; void main() { gl_Position a_position u_offset; v_positionWithOffset a_position u_offset; }片段着色器precision mediump float; varying vec4 v_positionWithOffset; void main() { // 将「裁剪空间坐标值」-1 1转换为「颜色空间坐标值」0 1。 vec4 color v_positionWithOffset * 0.5 0.5; gl_FragColor color; }上述示例本身在实际应用中没有太大意义——一般很少会直接把裁剪空间坐标原样传给片段着色器并当作颜色使用。但通过观察该示例产生的「中间色」可以直观地理解 varying 的插值究竟是怎么回事。在「WebGL 的工作原理」一章中varying 的插值机制有更直观的演示三个顶点的坐标0,-100、150,125、-175,100经矩阵变换后写入gl_Position同时被换算成v_color传给片段着色器见 webgl-how-it-works.mdWebGL 获取三个顶点的值之后通过插值将三角形光栅化片段着色器则对构成三角形的每一个像素各调用一次见 webgl-how-it-works.md。从源码结构看仓库中 webgl-2d-triangle-with-position-for-color.html 正是该讲解对应的可运行示例。GLSL 语言深入GLSL 是「Graphics Library Shader Language图形库着色器语言」的缩写正是编写着色器所用的语言。它具备一些在 JavaScript 中不常见的独特特性其设计目标就是针对图形光栅化过程中常见的数学计算。以下逐一介绍 GLSL 的核心语言特性。矩阵相关的数据类型GLSL 内置了vec2、vec3、vec4数据类型分别表示 2 个、3 个、4 个数据的组合同样地mat2、mat3、mat4用于表示 2x2、3x3、4x4 的矩阵。有了这些类型可以方便地写出诸如「矩阵乘以标量非矩阵的普通数值」的计算vec4 a vec4(1, 2, 3, 4); vec4 b a * 2.0; // b 的值为 vec4(2, 4, 6, 8)。矩阵与矩阵相乘、向量与矩阵相乘也都可以直接书写mat4 a ???; mat4 b ???; mat4 c a * b; vec4 v ???; vec4 y c * v;分量选择器Selectors与 swizzle各种 vec 类型变量拥有丰富的「选择器selector」。以vec4为例vec4 v;v.x与v.s、v.r、v[0]意义相同v.y与v.t、v.g、v[1]意义相同v.z与v.p、v.b、v[2]意义相同v.w与v.q、v.a、v[3]意义相同利用这一点可以轻松实现 vec 分量的重排称为swizzle不仅可以交换顺序还可以重复使用同一元素。例如v.yyyy等价于vec4(v.y, v.y, v.y, v.y)同理v.bgra等价于vec4(v.b, v.g, v.r, v.a)在构造 vec 或 mat 值时还可以一次性写入多个部分vec4(v.rgb, 1)等价于vec4(v.r, v.g, v.b, 1)另外vec4(1)会被解释为vec4(1, 1, 1, 1)严格的数据类型约束GLSL 是一种强类型语言对数据类型要求非常严格这一点常常让人踩坑float f 1; // 错误。1 是 int 类型不能把 int 值赋给 float 变量。正确写法是以下之一float f 1.0; // 使用 float 值即可 float f float(1) // 将整数值强制转换为 float 类型前面例子中的vec4(v.rgb, 1)之所以不会对1报错是因为vec4在内部对参数执行了类似float(1)的强制类型转换。丰富的内置函数GLSL 内置了大量函数其中许多可以同时处理多个分量。例如计算角度angle正弦sine的函数T sin(T angle)其中 T 可以是float、vec2、vec3、vec4中的任意类型。传入vec4类型的值时返回的也是vec4即对每个分量分别求正弦。具体而言若v是vec4类型vec4 s sin(v);等价于vec4 s vec4(sin(v.x), sin(v.y), sin(v.z), sin(v.w));有时某个参数是 float其余是 T此时该 float 会作用于所有分量。例如v1与v2为vec4类型、f为 float 类型时vec4 m mix(v1, v2, f);等价于vec4 m vec4( mix(v1.x, v2.x, f), mix(v1.y, v2.y, f), mix(v1.z, v2.z, f), mix(v1.w, v2.w, f));精度声明GLSL 还要求着色器尤其是片段着色器声明数值精度。片段着色器没有默认精度所以必须以precision mediump float;之类的语句开头顶点着色器通常可以使用默认精度但显式声明依然是良好的习惯。这一约定在仓库所有示例的片段着色器中都可见例如 webgl-fundamentals.html。关于精度选择highp/mediump/lowp的更多细节可参考仓库中的 webgl-qna-when-to-choose-highp--mediump--lowp-in-shaders.md 问答文章。总结将一切串起来这正是本系列 WebGL 讲义的核心要点WebGL 应用本质上就是「创建各种着色器 → 向着色器提供数据 → 调用gl.drawArrays或gl.drawElements」。调用之后WebGL 会自动完成为每一个顶点调用一次顶点着色器处理全部顶点为每一个像素调用一次片段着色器处理全部像素最终将像素绘制出来。以上就是 WebGL 的全部。仓库中的 webgl-fundamentals.html 用约 150 行代码完整演示了这一过程编译两个 GLSL 着色器、链接成程序、配置 attribute 缓冲区、调用一次gl.drawArrays即完成一次最简单的三角形绘制。通往下一课方向选择「创建着色器」需要编写若干行代码不过这段代码在绝大多数 WebGL 程序中都是通用且可复用的——写好一次之后基本无需再关心。关于「编译 GLSL 着色器并链接成着色器程序」的具体封装已在「WebGL 的模板代码Boilerplate」一章中专门讲解仓库中对应实现见 webgl-fundamentals.html 中的createShader与createProgram函数。接下来有两条路线可供选择对图像处理感兴趣下一步建议学习「WebGL 中的图像处理」对平移、旋转、缩放乃至最终的三维处理感兴趣请从「二维平移」开始该主题会逐步过渡到三维处理。在这两条路线中本文介绍的 attribute、uniform、texture、varying 四种数据通道与 GLSL 基础语法都是最核心的底层能力值得反复研读、动手验证。赞分享图形学【免费下载链接】webgl-fundamentalsWebGL lessons that start with the basics项目地址https://gitcode.com/gh_mirrors/we/webgl-fundamentals点击查看免费下载相关推荐WebGL 着色器与 GLSL 完全指南顶点着色器、片段着色器与着色器语言详解WebGL 着色器与 GLSL 完全指南顶点着色器、片段着色器与着色器语言详解 本文是 WebGL 基础 https://link.gitcode.com/i图形学Bruno Simon Folio 2019Three.js与GLSL着色器编程终极指南Bruno Simon Folio 2019Three.js与GLSL着色器编程终极指南 Bruno Simon Folio 2019是一个基于Three.j前端3D渲染Sigma.js的WebGL渲染原理顶点着色器与片元着色器深度剖析Sigma.js的WebGL渲染原理顶点着色器与片元着色器深度剖析 Sigma.js 是一个专注 图可视化 的 JavaScript 库能够借助 WebGL数据可视化前端图形学创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Android TextureView视频播放异常解决方案

Android TextureView视频播放异常解决方案

1. 问题现象与背景解析在Android应用开发中,TextureView作为SurfaceView的升级替代方案,因其支持动画变换和灵活布局的特性,已成为视频播放功能的首选组件。但在实际开发中,不少开发者会遇到一个看似简单却令人困惑的报错&#xf…

2026/10/13 9:36:25 阅读更多 →
Java二手交易平台毕设课设实战:从技术选型到部署避坑

Java二手交易平台毕设课设实战:从技术选型到部署避坑

简介:这是一套面向高校计算机相关专业学生的Java二手交易平台项目源码,适用于毕业设计、课程设计及个人练手场景,帮助解决从选题到落地实现完整业务系统的需求。项目基于JDK1.8与Spring Boot框架搭建,采用Maven管理依赖&#xff0…

2026/10/13 9:36:25 阅读更多 →
极简自动化部署工具:轻量化CI/CD实践指南

极简自动化部署工具:轻量化CI/CD实践指南

1. 项目概述:极简自动化部署工具的核心价值在持续交付成为标配的今天,部署效率直接决定团队生产力。传统CI/CD工具虽然功能强大,但存在两个致命痛点:一是学习曲线陡峭,二是资源消耗严重。这正是我们开发这款极轻量自动…

2026/10/13 9:36:25 阅读更多 →

最新新闻

大模型面试避坑指南:小白程序员如何通过真实项目斩获Offer(收藏版)

大模型面试避坑指南:小白程序员如何通过真实项目斩获Offer(收藏版)

本文基于作者上百场大模型面试经验,提炼出2026年大模型面试的核心考察点,包括Agent方向岗位增多、简历价值变化、准备时机等。文章强调“为可靠付钱”的行业趋势,建议候选人关注Agentic与推理能力、业务链路闭环、项目深度和评价维度变化。此…

2026/10/13 10:16:45 阅读更多 →
Windows Terminal 定制全攻略:配色、字体与提示符打造专属控制台

Windows Terminal 定制全攻略:配色、字体与提示符打造专属控制台

各位用 Windows 的朋友应该都有过这样的经历:每天打开系统自带的 CMD 或者 PowerShell 窗口,黑底白字,一条条命令敲下去,总觉得少了点什么。直到我第一次接触 Windows Terminal,才意识到原来控制台也可以玩出这么多花样…

2026/10/13 10:16:45 阅读更多 →
液压伺服电动机状态空间建模与控制器设计仿真

液压伺服电动机状态空间建模与控制器设计仿真

这个题目我太熟了,控制类和机电类专业做课设、毕设,十个里有八个会碰到“液压伺服电动机的状态空间设计与仿真”这种组合。对象是电液伺服系统,方法是现代控制理论的状态空间法,工具是Matlab。这套源程序迭代到14900期&#xff0c…

2026/10/13 10:16:45 阅读更多 →
Qt xcb插件加载失败原因与修复指南

Qt xcb插件加载失败原因与修复指南

1. 问题本质:这不是Qt程序崩溃,而是图形系统“失联”了你看到这行报错——This application failed to start because it could not find or load the Qt platform plugin xcb——第一反应可能是“Qt坏了”“程序编译错了”“Linux系统不兼容”。但实话讲…

2026/10/13 10:16:45 阅读更多 →
蓝屏0x10E显存管理内部错误:从驱动到BIOS的笔记本排查实战

蓝屏0x10E显存管理内部错误:从驱动到BIOS的笔记本排查实战

最近手头碰到一台新到的某品牌笔记本,配置是 Ultra X7 358H 处理器加独立显卡的组合,系统装好没两天就开始出问题。现象很典型:浏览网页或者看视频的时候突然画面卡住,随后屏幕一黑,直接蓝屏,错误代码0x10E…

2026/10/13 10:16:45 阅读更多 →
FFMpeg 4.1音频检测实战:命令详解与批处理自动化

FFMpeg 4.1音频检测实战:命令详解与批处理自动化

做内容生产这一年多,我处理过最多的活儿不是剪辑,而是给一堆音频素材做体检。尤其是接手播客剪辑、视频课程批量转码这类项目时,几十个甚至上百个文件摆在面前,里面哪个有静音、哪个音量忽大忽小、哪个爆音了,光靠耳朵…

2026/10/13 10:15:43 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →