webgl-插值渲染原理理解

问题背景

需要用webgl 优化 kriging.grid 物理计算量大的问题
在这里插入图片描述

解决思路

借助 GPU 的性能,采用 webgl 替代克里金的计算量大的方法

demo案例效果

矩阵转换–GLSL着色程序

注意glsl语句

void main() {
    // 将位置和矩阵相乘
    gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);

    // 从裁减空间转换到颜色空间
    // 裁减空间范围 -1.0 到 +1.0
    // 颜色空间范围 0.0 到 1.0  RGBA
    v_color = gl_Position * 0.5 + 0.5;// 实现 b 固定为0.5 ,a 固定为 1
    // v_color = vec4(gl_Position.x * 0.5 + 0.5,gl_Position.y * 0.5 + 0.5,0,1); // 改成b固定为0,a固定为 1  实现 越往上 g 越大,越往右 r 越大
  }

让我们从上例的三个顶点开始分析

我们的给顶点着色器施加了一个包含平移,旋转和缩放的的矩阵,并将结果转换到裁剪空间。 默认平移,旋转和缩放值为:平移 = 200, 150旋转 = 0缩放 = 1,所以这里只进行了平移。 画布大小(背景缓冲)为 400×300,所以三个顶点在裁剪空间中为以下坐标值。

写入 gl_Position 的值
在这里插入图片描述
同时将这些值转换到颜色空间中赋给我们定义的可变量v_color。
写入 v_color 的值
在这里插入图片描述
注意:这个怎么来的呢
在这里插入图片描述
利用这三个值进行插值后传进每个像素运行的片段着色器中。
在这里插入图片描述

index.vue

<!--/**
* @author: liuk
* @date: 2024-09-04
* @describe:webgl 工作原理
*/-->
<template>
  <div class="workingPrinciple1-box">
    <div class="block">
      <el-form-item label="x:" label-width="50">
        <el-slider v-model="translationx" :max="gl.canvas.width" @change="drawScene"/>
      </el-form-item>
      <el-form-item label="y:" label-width="50">
        <el-slider v-model="translationy" :max="gl.canvas.height" @change="drawScene"/>
      </el-form-item>
      <el-form-item label="angle:" label-width="50">
        <el-slider v-model="angleInRadians"  :max="360"  @change="drawScene"/>
      </el-form-item>
      <el-form-item label="scalex:" label-width="50">
        <el-slider v-model="scalex" :min="-5" :max="5" :step="0.01" @change="drawScene"/>
      </el-form-item>
      <el-form-item label="scaley:" label-width="50">
        <el-slider v-model="scaley" :min="-5" :max="5" :step="0.01" @change="drawScene"/>
      </el-form-item>
    </div>
  </div>

</template>

<script lang="ts" setup>
import {useWebglDemoStore} from "@/store/modules/webglDemo";
import {onBeforeUnmount, onMounted, reactive, toRefs} from "vue";
import m3 from "@/utils/m3.js"

const model = reactive({
  translationx: 200,
  translationy:150,
  angleInRadians: 0,
  scalex: 1,
  scaley: 1,
})
const {translationx, translationy, angleInRadians, scalex, scaley} = toRefs(model)

const webglDemoStore = useWebglDemoStore()
const gl = webglDemoStore.gl

/*
* 概念:
* WebGL在GPU上的工作基本上分为两部分
* 1.将顶点(或数据流)转换到裁剪空间坐标
* 2.基于第一部分的结果绘制像素点 - ”光栅化“
* */

onMounted(() => {
  init()
})

onBeforeUnmount(() => {
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
})

// 初始化
let program, positionBuffer, positionAttributeLocation, matrixLocation
const init = () => {
  // 创建两个着色器
  const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
  const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
  // 创建着色程序
  program = createProgram(gl, vertexShader, fragmentShader)
  // 从刚才创建的GLSL着色程序中找到 a_position 属性值所在的位置
  positionAttributeLocation = gl.getAttribLocation(program, "a_position");
  gl.enableVertexAttribArray(positionAttributeLocation);
  // 全局变量所在的位置
  matrixLocation = gl.getUniformLocation(program, "u_matrix");

  // 创建缓冲
  positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  // 三个二维点坐标,把 gl.bufferData 复制这些数据到GPU的 positionBuffer 对象上
  const positions = [0, -100, 150, 125, -175, 100];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

  drawScene()
}

// 创建GLSL字符串 - 多行模板文字方式
const vertexShaderSource = `
  attribute vec2 a_position;

  uniform mat3 u_matrix;

  varying vec4 v_color;

  void main() {
    // 将位置和矩阵相乘
    gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);

    // 从裁减空间转换到颜色空间
    // 裁减空间范围 -1.0 到 +1.0
    // 颜色空间范围 0.0 到 1.0  RGBA
    // v_color = gl_Position * 0.5 + 0.5;// 实现 b 固定为0.5 ,a 固定为 1
    v_color = vec4(gl_Position.x * 0.5 + 0.5,gl_Position.y * 0.5 + 0.5,0,1); // 改成b固定为0,a固定为 1  实现 越往上 g 越大,越往右 r 越大
  }
`
const fragmentShaderSource = `
  precision mediump float;

  varying vec4 v_color;

  void main() {
    gl_FragColor = v_color;
  }
`

// 创建着色器方法,输入参数:渲染上下文,着色器类型,数据源
const createShader = (gl, type, source) => {
  const shader = gl.createShader(type); // 创建着色器对象
  gl.shaderSource(shader, source); // 提供数据源
  gl.compileShader(shader); // 编译 -> 生成着色器
  const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  if (success) {
    return shader;
  }
  console.warn(gl.getShaderInfoLog(shader));
  gl.deleteShader(shader);
}

// 创建着色程序,将这两个着色器 link(链接)到一个 program(着色程序)
const createProgram = (gl, vertexShader, fragmentShader) => {
  const program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  const success = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (success) {
    return program;
  }
  console.warn(gl.getProgramInfoLog(program));
  gl.deleteProgram(program);
}

const drawScene = () => { // 渲染
  // 调整 canvas 尺寸
  gl.canvas.width = gl.canvas.clientWidth;
  gl.canvas.height = gl.canvas.clientHeight;
  // 渲染
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  // 清空画布
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  // 告诉它用我们之前写好的着色程序(一个着色器对)
  gl.useProgram(program);
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  // 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
  const size = 2;          // 每次迭代运行提取两个单位数据
  const type = gl.FLOAT;   // 每个单位的数据类型是32位浮点型
  const normalize = false; // 不需要归一化数据
  const stride = 0;        // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
  // 每次迭代运行运动多少内存到下一个数据开始点
  const offset = 0;        // 从缓冲起始位置开始读取
  gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset)

  // Compute the matrix
  console.log(model.translationx, model.translationy)
  var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
  matrix = m3.translate(matrix, model.translationx, model.translationy);
  matrix = m3.rotate(matrix, model.angleInRadians);
  matrix = m3.scale(matrix, model.scalex, model.scaley);

  // Set the matrix.
  gl.uniformMatrix3fv(matrixLocation, false, matrix);

  // WebGL运行我们的GLSL着色程序
  const primitiveType = gl.TRIANGLES; //图元类型
  const offset1 = 0;
  const count = 3; // 顶点着色器将运行三次
  gl.drawArrays(primitiveType, offset1, count);
}





</script>

<style lang="scss" scoped>
.workingPrinciple1-box {
  position: absolute;
  right: 20px;
  top: 80px;
  width: 250px;
  padding: 10px;
  box-sizing: border-box;
  background: #ccc;
}

:deep(.el-form-item) {
  margin-bottom: 0;

  .el-form-item__label {
    color: #000 !important;

  }
}
</style>
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值