WebGL进阶(四)-视点和视线

embedded/2024/10/30 19:48:16/

理论基础:

顶点着色器 Vertex Shader

主要是负责处理顶点位置、顶点颜色、顶点向量等顶点的数据;处理一些顶点的变换:例如在进行视图变换和投影变换时MVP矩阵会改变顶点的位置信息。

输入:

顶点着色器输入部分主要是声明:

①储存着每个顶点属性的attribute变量;

②所有顶点都会实现的(例如MVP矩阵、透视变换矩阵等)全局的uniform变量;

③一些会传递给片元着色器的varying变量。

javascript">// 变量
attribute vwc3 aVertexPosition; // 顶点位置
attribute vec3 aNormalPosition; // 法线
attribute vec2 aTextureCoord; // uv坐标varying highp vec2 vTextureCoord; // 顶点着色器传递给片元着色器
varying highp vec3 vFragPos;
varying highp vec3 vNormal;// 矩阵
uniform mat4 uModelViewMatrix; // 视图变换矩阵
uniform mat4 uProjectionMatrix; // 透视变换矩阵
输出:

①顶点坐标gl_Position;

②给输入部分声明的varying变量赋值;

矩阵运算从右往左运算

vFragPos = aVertexPosition;
vNormal = aNormalPosition;// 计算点坐标
gl_Position = uProjextionMatrix * uModelViewMatrix * vec4(aVertexPosition,1.0);vTextureCoord = aTextureCoord;
片元着色器 Fragment Shader

用于颜色、贴图、光照阴影等

javascript">//除了上述出现的“着色器不支持高精度,限定浮点类型为中精度”
precision mediump float;//还有表示支持高精度的:“着色器支持高精度,限定浮点类型为高精度”
precision highp float;//以及仅支持低精度的:
prcision lowp float; 
输入: 

uniform声明

javascript">/声明一个纹理相关的变量,sampler2D 也是一种数据类型
//它是一种取样器类型的变量,该变量对应传入的纹理2D图片的像素数据
uniform sampler2D uSampler;
//Blinn-Phong需要的漫反射项kd、高光项ks、光源位置、相机位置、光强
uniform vec3 uKd;
uniform vec3 uKs;
uniform vec3 uLightPos;
uniform vec3 uCameraPos;
uniform float uLightIntensity;
// 这个TextureSample参数作用应该是:
// 以左上角为原点,通过UV截取图片,如果==1,则正常取值
uniform int uTextureSample; 

 varing变量

javascript">//继承varying参数
varying highp vec2 vTextureCoord;
varying highp vec3 vFragPos;
varying highp vec3 vNormal; 
输出: 

一般设置为gl_FragColor

参考:GAMES101-现代计算机图形学入门-闫令琪_哔哩哔哩_bilibili

gl.compileShader(shader), 传递需要编译的着色器,函数实现着色器编译。
gl.getShaderParameter(shader,COMPILE_STATUS),COMPILE_STATUS为:

  • gl.DELETE_STATUS:标示着色器是否被删除,删除(GL_TRUE)未删除(GL_FALSE).
  • gl.COMPILE_STATUS: 标示着色器是否编译成功,是(GL_TRUE)不是(GL_FALSE
  • gl.SHADER_TYPE: 标示着色器类型,是顶点着色器 (gl.VERTEX_SHADER) 还是片段着色器 (gl.FRAGMENT_SHADER)
WebGLRenderingContext.enableVertexAttribArray()

在 WebGL 中,作用于顶点的数据会先储存在attributes。这些数据仅对 JavaScript 代码和顶点着色器可用。属性由索引号引用到 GPU 维护的属性列表中。在不同的平台或 GPU 上,某些顶点属性索引可能具有预定义的值。创建属性时,WebGL 层会分配其他属性。

类型为GLuint 的索引,指向要激活的顶点属性。可以使用getAttribLocation方法来获取索引.

WebGLRenderingContext.vertexAttribPointer(index,size,type,normalized,stride,offset)

打开属性数组列表中指定索引处的通用顶点属性数组,

index:定要修改的顶点属性的索引。

size:指定每个顶点属性的组成数量,必须是 1,2,3 或 4。

type:

指定数组中每个元素的数据类型可能是:

  • gl.BYTE: signed 8-bit integer, with values in [-128, 127] 有符号的 8 位整数,范围 [-128, 127]

  • gl.SHORT: signed 16-bit integer, with values in [-32768, 32767] 有符号的 16 位整数,范围 [-32768, 32767]

  • gl.UNSIGNED_BYTE: unsigned 8-bit integer, with values in [0, 255] 无符号的 8 位整数,范围 [0, 255]

  • gl.UNSIGNED_SHORT: unsigned 16-bit integer, with values in [0, 65535] 无符号的 16 位整数,范围 [0, 65535]

  • gl.FLOAT: 32-bit IEEE floating point number 32 位 IEEE 标准的浮点数

  • When using a WebGL 2 context, the following values are available additionally: 使用 WebGL2 版本的还可以使用以下值:

    • gl.HALF_FLOAT: 16-bit IEEE floating point number 16 位 IEEE 标准的浮点数

normalize:

         当转换为浮点数时是否应该将整数数值归一化到特定的范围。

  • or types gl.BYTE and gl.SHORT, normalizes the values to [-1, 1] if true. 对于类型gl.BYTEgl.SHORT,如果是 true 则将值归一化为 [-1, 1]
  • For types gl.UNSIGNED_BYTE and gl.UNSIGNED_SHORT, normalizes the values to [0, 1] if true. 对于类型gl.UNSIGNED_BYTEgl.UNSIGNED_SHORT,如果是 true 则将值归一化为 [0, 1]
  • For types gl.FLOAT and gl.HALF_FLOAT, this parameter has no effect. 对于类型gl.FLOATgl.HALF_FLOAT,此参数无效

stride:

一个 GLsizei,以字节为单位指定连续顶点属性开始之间的偏移量 (即数组中一行长度)。不能大于 255。如果 stride 为 0,则假定该属性是紧密打包的,即不交错属性,每个属性在一个单独的块中,下一个顶点的属性紧跟当前顶点之后。

offset:

指定顶点属性数组中第一部分的字节偏移量。必须是类型的字节长度的倍数。

WebGLRenderingContext.uniformMatrix[234]fv(location,transpose,value)

该方法的 3 个版本(uniformMatrix2fv()uniformMatrix3fv() 和 unifomMatrix4fv()),分别以二阶、三阶和四阶方阵作为输入值,它们应是分别具有 4、9、16 个浮点数的数组。

javascript">WebGLRenderingContext.uniformMatrix2fv(location, transpose, value);
WebGLRenderingContext.uniformMatrix3fv(location, transpose, value);
WebGLRenderingContext.uniformMatrix4fv(location, transpose, value);

location

WebGLUniformLocation 对象包含了要修改的 uniform attribute 位置。位置使用 getUniformLocation()获得。

transpose

GLboolean 指定是否转置矩阵。必须为 false

value

Float32Array 型或者是 GLfloat 序列值。这些值被假定按列主序(column major order)的方式提供。

 

源码:

键盘动态改变视点

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="glMatrix-0.9.6.min.js"></script><script>let vertexstring = `attribute vec4 a_position;uniform mat4 u_formMatrix;attribute vec4 a_color;varying vec4 color;void main(void){gl_Position =  u_formMatrix * a_position;color = a_color;}`;let fragmentstring = `precision mediump float;varying vec4 color;void main(void){gl_FragColor =color;}`;var webgl;var uniformTexture = 0;var uniformTexture1 = 0;var uniformAnim = 0;var count = 0;var texture0;var texture1;var angley=0;var anglex= 0;function webglStart() {init();tick()}function tick() {requestAnimFrame(tick);initBuffer();draw();}function init() {initWebgl();initShader();initEvent();}function handleKeyDown(event) {if (String.fromCharCode(event.keyCode) == 'W') {angley += 0.01;}else if (String.fromCharCode(event.keyCode) == 'S') {angley -= 0.01;}else if (String.fromCharCode(event.keyCode) == 'A') {anglex -= 0.01;}else if (String.fromCharCode(event.keyCode) == 'D') {anglex += 0.01;}}function initEvent() {document.onkeydown = handleKeyDown;}function initWebgl() {let webglDiv = document.getElementById('myCanvas');webgl = webglDiv.getContext("webgl");webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);}function initShader() {let vsshader = webgl.createShader(webgl.VERTEX_SHADER);let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);webgl.shaderSource(vsshader, vertexstring);webgl.shaderSource(fsshader, fragmentstring);webgl.compileShader(vsshader);webgl.compileShader(fsshader);if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(vsshader);alert(err);return;}if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(fsshader);alert(err);return;}let program = webgl.createProgram();webgl.attachShader(program, vsshader);webgl.attachShader(program, fsshader)webgl.linkProgram(program);webgl.useProgram(program);webgl.program = program}function initBuffer() {let ModelMatrix = mat4.create();mat4.identity(ModelMatrix);let angle = Math.PI / 180 * count;console.log(angle);mat4.rotate(ModelMatrix, angle, [0, 0, 1])let ViewMatrix = mat4.create();mat4.identity(ViewMatrix);debuggerViewMatrix = mat4.lookAt([anglex, angley, 0.2], [0, 0, 0], [0, 1, 0], ViewMatrix);console.log(ViewMatrix);let mvMatrix = mat4.multiply(ViewMatrix, ModelMatrix)let arr = [0.0, 0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,-0.5, -0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,0.5, -0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,0.5, 0.4, -0.2, 1, 1.0, 1.0, 0.4, 1,-0.5, 0.4, -0.2, 1, 1.0, 1.0, 0.4, 1,0.0, -0.6, -0.2, 1, 1.0, 1.0, 0.4, 1,0.0, 0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,-0.5, -0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,0.5, -0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,]let pointPosition = new Float32Array(arr);let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");let triangleBuffer = webgl.createBuffer();webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);webgl.enableVertexAttribArray(aPsotion);webgl.vertexAttribPointer(aPsotion, 4, webgl.FLOAT, false, 8 * 4, 0);let aColor = webgl.getAttribLocation(webgl.program, "a_color");webgl.enableVertexAttribArray(aColor);webgl.vertexAttribPointer(aColor, 4, webgl.FLOAT, false, 8 * 4, 4 * 4);let uniformMatrix1 = webgl.getUniformLocation(webgl.program, "u_formMatrix");webgl.uniformMatrix4fv(uniformMatrix1, false, mvMatrix)}function draw() {webgl.clearColor(0.0, 1.0, 0.0, 1.0);webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);webgl.enable(webgl.DEPTH_TEST);//纹理变动uniformAnim = webgl.getUniformLocation(webgl.program, "anim");count = count + 1;webgl.uniform1f(uniformAnim, count);webgl.drawArrays(webgl.TRIANGLES, 0, 9);}window.requestAnimFrame = (function () {return window.requestAnimationFrame ||window.webkitRequestAnimationFrame ||window.mozRequestAnimationFrame ||window.oRequestAnimationFrame ||window.msRequestAnimationFrame ||function (callback, element) {window.setTimeout(callback, 1000 / 60);};})();</script>
</head><body onload="webglStart()"><canvas id='myCanvas' width="1024" height='768'></canvas>
</body></html>

模型矩阵与视图矩阵共同作用 :

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="./gl-matrix.js"></script><script>let vertexstring = `attribute vec4 a_position;uniform mat4 u_formMatrix;uniform mat4 proj;attribute vec4 a_color;varying vec4 color;void main(void){gl_Position =  u_formMatrix * a_position;color = a_color;}`;let fragmentstring = `precision mediump float;varying vec4 color;void main(void){gl_FragColor =color;}`;var webgl;var uniformTexture = 0;var uniformTexture1 = 0;var uniformAnim = 0;var count = 0;var texture0;var texture1;function webglStart() {init();}function init() {initWebgl();initShader();initBuffer();draw();initEvent()}function initWebgl() {let webglDiv = document.getElementById('myCanvas');webgl = webglDiv.getContext("webgl");webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);}function initShader() {let vsshader = webgl.createShader(webgl.VERTEX_SHADER);let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);webgl.shaderSource(vsshader, vertexstring);webgl.shaderSource(fsshader, fragmentstring);webgl.compileShader(vsshader);webgl.compileShader(fsshader);if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(vsshader);alert(err);return;}if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(fsshader);alert(err);return;}let program = webgl.createProgram();webgl.attachShader(program, vsshader);webgl.attachShader(program, fsshader)webgl.linkProgram(program);webgl.useProgram(program);webgl.program = program}function initBuffer() {let ModelMatrix = glMatrix.mat4.create();glMatrix.mat4.identity(ModelMatrix);let angle = Math.PI / 180 * 30;console.log(angle);glMatrix.mat4.rotate(ModelMatrix, ModelMatrix,angle, [0, 0, 1])let ViewMatrix = glMatrix.mat4.create();glMatrix.mat4.identity(ViewMatrix);debuggerViewMatrix =glMatrix.mat4.lookAt(ViewMatrix,[0, 0, 0.3], [0, 0, 0], [0, 1, 0]);console.log(ViewMatrix);let mvMatrix = glMatrix.mat4.create();glMatrix.mat4.multiply(mvMatrix,ViewMatrix, ModelMatrix)let arr = [0.0, 0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,-0.5, -0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,0.5, -0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,0.5, 0.4, -0.2, 1, 1.0, 1.0, 0.4, 1,-0.5, 0.4, -0.2, 1, 1.0, 1.0, 0.4, 1,0.0, -0.6, -0.2, 1, 1.0, 1.0, 0.4, 1,0.0, 0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,-0.5, -0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,0.5, -0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,]let pointPosition = new Float32Array(arr);let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");let triangleBuffer = webgl.createBuffer();webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);webgl.enableVertexAttribArray(aPsotion);webgl.vertexAttribPointer(aPsotion, 4, webgl.FLOAT, false, 8 * 4, 0);let aColor = webgl.getAttribLocation(webgl.program, "a_color");webgl.enableVertexAttribArray(aColor);webgl.vertexAttribPointer(aColor, 4, webgl.FLOAT, false, 8 * 4, 4 * 4);let uniformMatrix1 = webgl.getUniformLocation(webgl.program, "u_formMatrix");webgl.uniformMatrix4fv(uniformMatrix1, false, mvMatrix)}function draw() {webgl.clearColor(0.0, 1.0, 0.0, 1.0);webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);// webgl.enable(webgl.DEPTH_TEST);//纹理变动uniformAnim = webgl.getUniformLocation(webgl.program, "anim");count = count + 0.01;webgl.uniform1f(uniformAnim, count);webgl.drawArrays(webgl.TRIANGLES, 0, 9);}</script>
</head><body onload="webglStart()"><canvas id='myCanvas' width="1024" height='768'></canvas>
</body></html>

视点改变影响三维展示效果:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="./gl-matrix.js"></script><script>let vertexstring = `attribute vec4 a_position;uniform mat4 u_formMatrix;attribute vec4 a_color;varying vec4 color;void main(void){gl_Position = u_formMatrix * a_position;color = a_color;}`;let fragmentstring = `precision mediump float;varying vec4 color;void main(void){gl_FragColor =color;}`;var webgl;var uniformTexture = 0;var uniformTexture1 = 0;var uniformAnim = 0;var count = 0;var texture0;var texture1;var projMatrix = glMatrix.mat4.create()function init() {initWebgl();initShader();initBuffer();draw();}function initWebgl() {let webglDiv = document.getElementById('myCanvas');webgl = webglDiv.getContext("webgl");}function initShader() {let vsshader = webgl.createShader(webgl.VERTEX_SHADER);let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);webgl.shaderSource(vsshader, vertexstring);webgl.shaderSource(fsshader, fragmentstring);webgl.compileShader(vsshader);webgl.compileShader(fsshader);if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(vsshader);alert(err);return;}if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(fsshader);alert(err);return;}let program = webgl.createProgram();webgl.attachShader(program, vsshader);webgl.attachShader(program, fsshader)webgl.linkProgram(program);webgl.useProgram(program);webgl.program = program}function initBuffer() {let arr = [0.0, 0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,-0.5, -0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,0.5, -0.5, -0.4, 1, 0.4, 1.0, 0.4, 1,0.5, 0.4, -0.2, 1, 1.0, 1.0, 0.4, 1,-0.5, 0.4, -0.2, 1, 1.0, 1.0, 0.4, 1,0.0, -0.6, -0.2, 1, 1.0, 1.0, 0.4, 1,0.0, 0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,-0.5, -0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,0.5, -0.5, 0.0, 1, 0.4, 0.4, 1.0, 1,// 0.0, 0.6, -0.4, 1,    0.4, 1.0, 1,1, // The back green one// -0.5, -0.4, -0.4,  1, 0.4, 1.0, 1,1,// 0.5, -0.4, -0.4, 1,  0.4, 1.0,1,1,// 0.5, 0.4, -0.2,  1, 1.0, 1.0, 0.4,1, // The middle yellow one// -0.5, 0.4, -0.2, 1,  1.0, 1.0, 0.4,1,// 0.0, -0.6, -0.2,1,   1.0, 1.0, 0.4,1,// 0.0, 0.5, 0.0,  1,   0.4, 0.4, 1.0,1, // The front blue one // -0.5, -0.5, 0.0,1,  0.4, 0.4, 1.0,1,// 0.5, -0.5, 0.0, 1,  0.4, 0.4, 1.0,1,]let pointPosition = new Float32Array(arr);let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");let triangleBuffer = webgl.createBuffer();webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);webgl.enableVertexAttribArray(aPsotion);webgl.vertexAttribPointer(aPsotion, 4, webgl.FLOAT, false, 8 * 4, 0);let aColor = webgl.getAttribLocation(webgl.program, "a_color");webgl.enableVertexAttribArray(aColor);webgl.vertexAttribPointer(aColor, 4, webgl.FLOAT, false, 8 * 4, 4 * 4);let modelView = glMatrix.mat4.create();glMatrix.mat4.identity(modelView);debuggermodelView = glMatrix.mat4.lookAt(modelView, [0, 0, 0.2], [0, 0, 0], [0, 1, 0]);console.log(modelView);let uniformMatrix1 = webgl.getUniformLocation(webgl.program, "u_formMatrix");webgl.uniformMatrix4fv(uniformMatrix1, false, modelView);}function draw() {webgl.clearColor(0.0, 1.0, 0.0, 1.0);webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);webgl.drawArrays(webgl.TRIANGLES, 0, 9);}</script>
</head><body onload="init()"><canvas id='myCanvas' width="1024" height='768'></canvas>
</body></html>

效果:

复盘: 


http://www.ppmy.cn/embedded/133686.html

相关文章

Spark SQL DSL

1、 Spark sql -- 代替hive的&#xff08;并非完全代替&#xff09; (1) Spark sql 和 hive 区别 : 两者都是写sql的&#xff0c;区别是计算引擎不一样 hive -- 计算引擎是MapReduce &#xff0c;是通过MR做计算的 Spark sql -- 计算引擎是Saprk Core&#xff…

云腾五洲的智联引擎是什么?

智联引擎是成都云腾五洲科技有限公司旗下的数智化转型服务平台&#xff0c;它提供云边协同的分布式物联网平台引擎服务。这一平台以其强大的功能和灵活性&#xff0c;为全行业提供数智化转型的新动力&#xff0c;帮助企业在数智化升级中实现持续增长。 核心能力 智联引擎的核心…

在面试了些外包以后,我有了些自己的思考

大家好&#xff0c;我是洋子&#xff0c;最近公司在降本增效&#xff0c;需要把外包从北京迁移到陕西的某新一线城市&#xff0c;其实就是变相裁员&#xff0c;减少外包的成本&#xff0c;裁掉现有的员工&#xff0c;重新招聘新人 在整个测试行业&#xff0c;外包测试的比重是…

STM32F103C8T6 IO 操作

1.开启相关时钟 在 STM32 微控制器中&#xff0c;开启 GPIO 端口的时钟是确保 IO 口可以正常工作的第一步。 查找 RCC 寄存器使能时钟 在 STM32 中&#xff0c;时钟控制的寄存器通常位于 RCC (Reset and Clock Control) 模块中。不同的 STM32 系列&#xff08;如 STM32F1、STM…

【Windows电脑通过cmd命令查看电脑电池健康度】

Cmd输入 powercfg /batteryreport打印电池使用报告&#xff0c;打开即可 此处查看FULL CHARGE CAPACITY&#xff08;充满电容量&#xff09;

研二了,该想想做啥呢?

写于22年10月24日&#xff0c;之前删了&#xff0c;今天回看&#xff0c;自己当时还挺有意思哈哈哈 一、自我介绍 行秋&#xff0c;男&#xff0c;24岁&#xff0c;电子信息专业&#xff0c;硕士在读。 二、新学期目标 1、好好学习 ① 学好一门语言&#xff0c;学好英语&am…

提升RAG系统的回答质量:PDF解析代码详解-PdfParser核心流程

在上一篇文章中&#xff0c;我们探讨了如何通过计算机视觉大模型提升RAG系统解析PDF文档的能力&#xff0c;并展示了该技术在行业文档识别中的实际应用效果。文章发布后&#xff0c;受到了广泛关注。读者主要关心两个问题&#xff1a;其一&#xff0c;如何在PDF文档识别过程中编…

推荐一款多显示器屏幕亮度调节工具:Twinkle Tray

Twinkle Tray中文版使您可以轻松管理多台显示器的亮度级别。 尽管 Windows 10 能够调节大多数显示器的背光&#xff0c;但它通常不支持外部显示器。 Windows 还缺乏管理多台显示器的亮度的任何功能。 该应用程序将一个新图标插入系统托盘&#xff0c;您可以在其中单击以立即访问…