求基于顶点UV/颜色的Flutter着色器示例
Flutter 顶点UV/颜色着色器示例(结合Canvas.drawVertices)
下面是一个完整的示例,展示如何通过Canvas.drawVertices()将顶点UV、颜色数据直接传入着色器,摆脱对全局uSize变量的依赖,实现多区域独立渲染:
1. GLSL 着色器代码
顶点着色器(vertex_shader.glsl)
uniform mat4 uViewProjectionMatrix; attribute vec2 aPosition; attribute vec2 aTexCoord; attribute vec4 aColor; varying vec2 vTexCoord; varying vec4 vColor; void main() { gl_Position = uViewProjectionMatrix * vec4(aPosition, 0.0, 1.0); vTexCoord = aTexCoord; vColor = aColor; }
片元着色器(fragment_shader.glsl)
precision mediump float; varying vec2 vTexCoord; varying vec4 vColor; void main() { // 基于UV实现棋盘格效果,同时叠加顶点颜色 float checker = step(0.5, mod(vTexCoord.x + vTexCoord.y, 1.0)); vec3 checkerColor = mix(vec3(0.2), vec3(0.8), checker); gl_FragColor = vec4(checkerColor * vColor.rgb, vColor.a); }
2. Flutter Dart 代码实现
import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class VertexShaderDemo extends StatefulWidget { const VertexShaderDemo({super.key}); @override State<VertexShaderDemo> createState() => _VertexShaderDemoState(); } class _VertexShaderDemoState extends State<VertexShaderDemo> { FragmentProgram? _program; @override void initState() { super.initState(); _loadShader(); } Future<void> _loadShader() async { final vertexShader = await rootBundle.loadString('shaders/vertex_shader.glsl'); final fragmentShader = await rootBundle.loadString('shaders/fragment_shader.glsl'); _program = await FragmentProgram.fromAsset( 'shaders/fragment_shader.glsl', vertexShader: vertexShader, ); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( body: CustomPaint( painter: _VertexPainter(program: _program), size: Size.infinite, ), ); } } class _VertexPainter extends CustomPainter { final FragmentProgram? program; _VertexPainter({this.program}); @override void paint(Canvas canvas, Size size) { if (program == null) return; final shader = program!.fragmentShader(); // 定义两个独立矩形的顶点数据(每个矩形由2个三角形组成) final vertices = Vertices( VertexMode.triangles, [ // 左上矩形顶点 Offset(50, 50), Offset(250, 50), Offset(250, 250), Offset(50, 50), Offset(250, 250), Offset(50, 250), // 右下矩形顶点 Offset(300, 300), Offset(500, 300), Offset(500, 500), Offset(300, 300), Offset(500, 500), Offset(300, 500), ], uv: [ // 左上矩形UV(0-1范围) const Offset(0, 0), const Offset(1, 0), const Offset(1, 1), const Offset(0, 0), const Offset(1, 1), const Offset(0, 1), // 右下矩形UV(0-2范围,实现棋盘格重复) const Offset(0, 0), const Offset(2, 0), const Offset(2, 2), const Offset(0, 0), const Offset(2, 2), const Offset(0, 2), ], colors: [ // 左上矩形顶点颜色(蓝色系渐变) Colors.blue.withOpacity(0.8), Colors.lightBlue.withOpacity(0.8), Colors.cyan.withOpacity(0.8), Colors.blue.withOpacity(0.8), Colors.cyan.withOpacity(0.8), Colors.lightBlue.withOpacity(0.8), // 右下矩形顶点颜色(红色系渐变) Colors.red.withOpacity(0.8), Colors.orange.withOpacity(0.8), Colors.yellow.withOpacity(0.8), Colors.red.withOpacity(0.8), Colors.yellow.withOpacity(0.8), Colors.orange.withOpacity(0.8), ], ); // 设置视图投影矩阵,将画布坐标转为GL标准化设备坐标 shader.setFloatMatrix4(0, Float64List.fromList([ 2 / size.width, 0, 0, 0, 0, -2 / size.height, 0, 0, 0, 0, 1, 0, -1, 1, 0, 1, ])); canvas.drawVertices(vertices, BlendMode.srcOver, Paint()..shader = shader); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }
关键说明
- 顶点属性映射:Flutter 内置了顶点属性绑定,
Vertices的positions对应着色器aPosition,uv对应aTexCoord,colors对应aColor,无需手动绑定。 - 多区域独立渲染:示例中两个矩形的UV范围、颜色完全独立,着色器直接基于每个顶点的UV和颜色计算像素,不需要全局
uSize统一控制。 - 坐标转换:通过
uViewProjectionMatrix将Flutter画布坐标转换为GL的标准化设备坐标,确保顶点位置正确显示在屏幕上。
内容的提问来源于stack exchange,提问作者Andrey Falkov
相关产品推荐
相关产品推荐

