网站开发登录要做哪些验证ai制作网页
2026/4/17 8:39:32 网站建设 项目流程
网站开发登录要做哪些验证,ai制作网页,百度小程序模板,模板网站首页设计大家好#xff0c;我是CC#xff0c;在这里欢迎大家的到来#xff5e; 背景 最近业务上需要个滤镜功能#xff0c;高级点的且可以直接应用的那种#xff0c;但是 Fabric.js 上只提供了基础滤镜#xff08;像黑白、复古等等#xff09;和自定义滤镜#xff08;调节单个…大家好我是CC在这里欢迎大家的到来背景最近业务上需要个滤镜功能高级点的且可以直接应用的那种但是 Fabric.js 上只提供了基础滤镜像黑白、复古等等和自定义滤镜调节单个参数像亮度、对比度、饱和度等等。原本设想通过组合各个参数设定好来滤镜使用但是很快就被现实打败——找不到合适的参数数据自己也不动设计。后经人指导可以基于 LUT 进行实现。LUTLookup Table查找表是一种颜色映射技术通过预定义的颜色转换规则将输入颜色映射到输出颜色。这是一张 LUT 图基于这张图实现的滤镜的逻辑大概是颜色映射通过查找表将输入颜色映射到输出颜色3D到2D转换将3D颜色空间展开为2D纹理存储三线性插值在3D颜色空间中进行插值计算GPU加速利用WebGPU的并行计算能力高效处理每个像素快速入门WebGPU先看图理解上边是浏览器的 WebGPU 对象通过 Adapter适配器来匹配底层系统电脑真实存在的物理设备的 GPU后续调用的也是 GPU 的原生 API。下面介绍 WebGPU 常用 API 和原理。调用WebGPU 对象支持在 window 中调用也可以在 worker 中调用。Navigator.gpu WorkerNavigator.gpu获取适配器const adapter await Navigator.gpu.requestAdapter() const adapter await Navigator.gpu.requestAdapter({ powerPreference: low-power // 默认值也可选 high-performance(非必要使用) })获取 GPU Deviceconst adapter await Navigator.gpu.requestAdapter() const device await adapter.requestDevice({ defaultQueue: , label: , requiredFeatures: [], requiredLimits: })管线和着色器管线pipeline是一个逻辑结构可通过编程完成程序工作。目前管线包括渲染管线和计算管线渲染管线用于图形渲染计算管线用于通用计算。渲染管线包括两个阶段顶点着色阶段和片元着色阶段。使用生产机器人的流水线来比喻 WebGPU、pipeline 和 shader 之间的关系。┌─────────────────────────────────────────────┐ │ WebGPU 工厂 │ │ ┌──────────────────────────────────────┐ │ │ │ Pipeline 生产线 │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ 顶点着色器 │ │片段着色器 │ │ │ │ │ │(车体成型机器人) │ │(喷漆机器人) │ │ │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ ↓ ↓ │ │ │ │ 车架成型 上色完成 │ │ │ └──────────────────────────────────────┘ │ └─────────────────────────────────────────────┘ 关键关系 1. 工厂WebGPU提供生产环境 2. 生产线Pipeline定义生产流程 3. 机器人Shader执行具体任务 4. 三者缺一不可协同工作着色器使用WebGPU 着色器语言是用称为 WebGPU 着色器语言WGSL的低级的类 Rust 语言编写的。建立渲染管线顶点着色阶段vertex代码块接受包含位置和颜色的数据分块根据给定的位置定位顶点插入颜色然后将数据传入到片元着色器阶段。片元着色阶段fragment代码块接受来自顶点着色器阶段的数据并根据给定的颜色为顶点着色。const shaders struct VertexOut { builtin(position) position : vec4f, location(0) color : vec4f } vertex fn vertex_main(location(0) position: vec4f, location(1) color: vec4f) - VertexOut { var output : VertexOut; output.position position; output.color color; return output; } fragment fn fragment_main(fragData: VertexOut) - location(0) vec4f { return fragData.color; } ; const shaderModule device.createShaderModule({ code: shaders, }); const pipelineDescriptor { vertex: { module: shaderModule, entryPoint: vertex_main, buffers: vertexBuffers, }, fragment: { module: shaderModule, entryPoint: fragment_main, targets: [ { format: navigator.gpu.getPreferredCanvasFormat(), }, ], }, primitive: { topology: triangle-list, }, layout: auto, }; const renderPipeline device.createRenderPipeline(pipelineDescriptor);创建缓冲区const vertices new Float32Array([ 0.0, 0.6, 0, 1, 1, 0, 0, 1, -0.5, -0.6, 0, 1, 0, 1, 0, 1, 0.5, -0.6, 0, 1, 0, 0, 1, 1, ]); const vertexBuffer device.createBuffer({ size: vertices.byteLength, // make it big enough to store vertices in usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, }); device.queue.writeBuffer(vertexBuffer, 0, vertices, 0, vertices.length);配置 canvas 上下文const canvas document.querySelector(#gpuCanvas); const context canvas.getContext(webgpu); context.configure({ device: device, format: navigator.gpu.getPreferredCanvasFormat(), // 纹理texture格式 alphaMode: premultiplied, // 半透明纹理时使用的 alpha 模式 });运行渲染管线const commandEncoder device.createCommandEncoder(); const textureView context.getCurrentTexture().createView(); const renderPassDescriptor { colorAttachments: [ { clearValue: { r: 0.0, g: 0.5, b: 1.0, a: 1.0 }, loadOp: clear, storeOp: store, view: textureView, }, ], }; const passEncoder commandEncoder.beginRenderPass(renderPassDescriptor); const passEncoder commandEncoder.beginRenderPass(renderPassDescriptor); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, bindGroup); passEncoder.draw(6); passEncoder.end(); device.queue.submit([commandEncoder.finish()]);滤镜实现基础类export class WebGPUFilter { private canvas: HTMLCanvasElement; private device: GPUDevice | null null; private context: GPUCanvasContext | null null; private pipeline: GPURenderPipeline | null null; private sampler: GPUSampler | null null; constructor() { this.canvas document.createElement(canvas); } public async init() { if (!navigator.gpu) { console.error(WebGPU not supported); throw new Error(WebGPU not supported); } const adapter await navigator.gpu.requestAdapter(); if (!adapter) { console.error(No WebGPU adapter found); throw new Error(No WebGPU adapter found); } this.device await adapter.requestDevice(); this.context this.canvas.getContext(webgpu); if (!this.context) { throw new Error(WebGPU context not found); } const presentationFormat navigator.gpu.getPreferredCanvasFormat(); this.context.configure({ device: this.device, format: presentationFormat, alphaMode: premultiplied, }); // Create shader module const shaderModule this.device.createShaderModule({ label: Filter Shader, code: struct VertexOutput { builtin(position) Position : vec4f, location(0) v_texCoord : vec2f, } vertex fn vs_main(builtin(vertex_index) vertexIndex : u32) - VertexOutput { var pos arrayvec2f, 6( vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(-1.0, 1.0), vec2f(-1.0, 1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0) ); // UVs: Top-Left origin for WebGPU textures usually // But we need to match how the image is drawn. // Standard quad: // (-1,-1) - (0, 1) in GL if 0,0 is bottom left. // In WebGPU, texture coords 0,0 is top-left. // If we map (-1,-1) [bottom-left on screen] to (0, 1) [bottom-left in texture uv], it matches. var tex arrayvec2f, 6( vec2f(0.0, 1.0), vec2f(1.0, 1.0), vec2f(0.0, 0.0), vec2f(0.0, 0.0), vec2f(1.0, 1.0), vec2f(1.0, 0.0) ); var output : VertexOutput; output.Position vec4f(pos[vertexIndex], 0.0, 1.0); output.v_texCoord tex[vertexIndex]; return output; } struct Uniforms { intensity : f32, grid_size : f32, } group(0) binding(0) var u_image : texture_2df32; group(0) binding(1) var u_image_sampler : sampler; group(0) binding(2) var u_lut : texture_2df32; group(0) binding(3) var u_lut_sampler : sampler; group(0) binding(4) varuniform uniforms : Uniforms; fragment fn fs_main(location(0) v_texCoord : vec2f) - location(0) vec4f { let color textureSample(u_image, u_image_sampler, v_texCoord); let blueColor color.b * (uniforms.grid_size * uniforms.grid_size - 1.0); var quad1 : vec2f; quad1.y floor(floor(blueColor) / uniforms.grid_size); quad1.x floor(blueColor) - (quad1.y * uniforms.grid_size); var quad2 : vec2f; quad2.y floor(ceil(blueColor) / uniforms.grid_size); quad2.x ceil(blueColor) - (quad2.y * uniforms.grid_size); let N uniforms.grid_size * uniforms.grid_size; let halfPixel 0.5 / N; let scale (N - 1.0) / N; let r color.r * scale halfPixel; let g color.g * scale halfPixel; var texPos1 : vec2f; texPos1.x (quad1.x r) / uniforms.grid_size; texPos1.y (quad1.y g) / uniforms.grid_size; var texPos2 : vec2f; texPos2.x (quad2.x r) / uniforms.grid_size; texPos2.y (quad2.y g) / uniforms.grid_size; let newColor1 textureSample(u_lut, u_lut_sampler, texPos1); let newColor2 textureSample(u_lut, u_lut_sampler, texPos2); let newColor mix(newColor1, newColor2, fract(blueColor)); let finalColor mix(color.rgb, newColor.rgb, uniforms.intensity); return vec4f(finalColor, color.a); } , }); this.pipeline this.device.createRenderPipeline({ label: Filter Pipeline, layout: auto, vertex: { module: shaderModule, entryPoint: vs_main, }, fragment: { module: shaderModule, entryPoint: fs_main, targets: [{ format: presentationFormat }], }, primitive: { topology: triangle-list, }, }); this.sampler this.device.createSampler({ magFilter: linear, minFilter: linear, }); } private async createTextureFromImage( image: HTMLImageElement | HTMLCanvasElement, flipY false ): PromiseGPUTexture { if (!this.device) throw new Error(Device not initialized); const texture this.device.createTexture({ size: [image.width, image.height], format: rgba8unorm, usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT, }); const source await createImageBitmap(image, { imageOrientation: flipY ? flipY : none, premultiplyAlpha: none, }); this.device.queue.copyExternalImageToTexture({ source: source }, { texture: texture }, [ image.width, image.height, ]); return texture; } public async apply( sourceImage: HTMLImageElement, lutImage: HTMLImageElement, intensity: number ): PromiseHTMLCanvasElement { if (!this.device || !this.context || !this.pipeline || !this.sampler) { // Attempt to init if not ready? Or just return canvas. // For simplicity, assume init() called. // If not, try init if (!this.device) await this.init(); if (!this.device) return this.canvas; } const device this.device!; const context this.context!; const pipeline this.pipeline!; const sampler this.sampler!; // Resize canvas if (this.canvas.width ! sourceImage.width || this.canvas.height ! sourceImage.height) { this.canvas.width sourceImage.width; this.canvas.height sourceImage.height; // Re-configure context after resize context.configure({ device: device, format: navigator.gpu.getPreferredCanvasFormat(), alphaMode: premultiplied, }); } // Determine grid size let gridSize 8.0; if (lutImage.width 256) { gridSize 4.0; } // Create Textures // WebGL implementation had sourceImage flipped Y (flipYtrue) // and LUT not flipped (flipYfalse). // In WebGPU, texture origin is top-left. Our UVs map bottom-left screen to (0,1) texture (bottom-left). // So we should NOT flip the source image, otherwise (0,0) becomes bottom-left in memory, // and UV (0,0) [top-left screen] would sample bottom-left of image. const imageTexture await this.createTextureFromImage(sourceImage, false); const lutTexture await this.createTextureFromImage(lutImage, false); // Create Uniform Buffer const uniformBufferSize 8; // 2 floats * 4 bytes const uniformBuffer device.createBuffer({ size: uniformBufferSize, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }); const uniformData new Float32Array([intensity, gridSize]); device.queue.writeBuffer(uniformBuffer, 0, uniformData); // Bind Group const bindGroup device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: imageTexture.createView() }, { binding: 1, resource: sampler }, { binding: 2, resource: lutTexture.createView() }, { binding: 3, resource: sampler }, { binding: 4, resource: { buffer: uniformBuffer } }, ], }); const commandEncoder device.createCommandEncoder(); const textureView context.getCurrentTexture().createView(); const renderPassDescriptor: GPURenderPassDescriptor { colorAttachments: [ { view: textureView, clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }, loadOp: clear, storeOp: store, }, ], }; const passEncoder commandEncoder.beginRenderPass(renderPassDescriptor); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, bindGroup); passEncoder.draw(6); passEncoder.end(); device.queue.submit([commandEncoder.finish()]); // Cleanup resources to avoid memory leaks? // In WebGPU, JS GC handles wrappers, but we might want to destroy textures manually if we create them every frame. // However, apply returns a canvas that has the content. // If we destroy imageTexture and lutTexture immediately, it might be fine because commands are submitted. // But lets let GC handle it for now unless performance is an issue. // Note: The canvas is now drawn. return this.canvas; } }使用// 初始化类 const filter new WebGPUFilter(); await filter.init(); webgpuFilterRef.value filter; // 应用滤镜 webgpuFilterRef.value .apply(img, filterImg, opacity) .then((result) resolve(result))注意目前 WebGPU 在浏览器兼容性还存在一些问题使用时需要特殊考虑场景。参考文档MDN WebGPU API

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询