My Code
`
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gl/flutter_gl.dart';
import 'package:flutter_gl/openGL/opengl/opengl_es_bindings/opengl_es_bindings.dart';
import 'package:vector_math/vector_math.dart' as math;
class ExampleCube extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
late FlutterGlPlugin flutterGlPlugin;
int? fboId;
num dpr = 1.0;
late double width;
late double height;
ui.Size? screenSize;
dynamic glProgram;
dynamic _vao;
dynamic _ebo;
dynamic _texture1;
dynamic _texture2;
dynamic sourceTexture;
dynamic defaultFramebuffer;
dynamic defaultFramebufferTexture;
int n = 0;
int t = DateTime.now().millisecondsSinceEpoch;
@OverRide
void initState() {
super.initState();
print(" init state..... ");
}
// Platform messages are asynchronous, so we initialize in an async method.
Future initPlatformState() async {
width = screenSize!.width;
height = width;
flutterGlPlugin = FlutterGlPlugin();
Map<String, dynamic> _options = {
"antialias": true,
"alpha": false,
"width": width.toInt(),
"height": height.toInt(),
"dpr": dpr
};
await flutterGlPlugin.initialize(options: _options);
print(" flutterGlPlugin: textureid: ${flutterGlPlugin.textureId} ");
setState(() {});
// web need wait dom ok!!!
Future.delayed(Duration(milliseconds: 100), () {
setup();
});
}
setup() async {
// web no need use fbo
if (!kIsWeb) {
await flutterGlPlugin.prepareContext();
setupDefaultFBO();
sourceTexture = defaultFramebufferTexture;
}
prepare();
}
initSize(BuildContext context) {
if (screenSize != null) {
return;
}
final mq = MediaQuery.of(context);
screenSize = mq.size;
dpr = mq.devicePixelRatio;
print(" screenSize: ${screenSize} dpr: ${dpr} ");
initPlatformState();
}
@OverRide
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Example app'),
),
body: Builder(
builder: (BuildContext context) {
initSize(context);
return SingleChildScrollView(child: _build(context));
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
clickRender();
},
child: Text("Render"),
),
),
);
}
Widget _build(BuildContext context) {
return Column(
children: [
Container(
width: width,
height: width,
color: Colors.black,
child: Builder(builder: (BuildContext context) {
if (kIsWeb) {
return flutterGlPlugin.isInitialized
? HtmlElementView(
viewType: flutterGlPlugin.textureId!.toString())
: Container();
} else {
return flutterGlPlugin.isInitialized
? Texture(textureId: flutterGlPlugin.textureId!)
: Container();
}
})),
],
);
}
setupDefaultFBO() {
final _gl = flutterGlPlugin.gl;
int glWidth = (width * dpr).toInt();
int glHeight = (height * dpr).toInt();
print("glWidth: ${glWidth} glHeight: ${glHeight} ");
defaultFramebuffer = _gl.createFramebuffer();
defaultFramebufferTexture = _gl.createTexture();
_gl.activeTexture(_gl.TEXTURE0);
_gl.bindTexture(_gl.TEXTURE_2D, defaultFramebufferTexture);
_gl.texImage2D(_gl.TEXTURE_2D, 0, _gl.RGBA, glWidth, glHeight, 0, _gl.RGBA,
_gl.UNSIGNED_BYTE, null);
_gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.LINEAR);
_gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.LINEAR);
_gl.bindFramebuffer(_gl.FRAMEBUFFER, defaultFramebuffer);
_gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0,
_gl.TEXTURE_2D, defaultFramebufferTexture, 0);
}
clickRender() {
print(" click render ... ");
render();
}
render() {
final _gl = flutterGlPlugin.gl;
_gl.viewport(0, 0, (width * dpr).toInt(), (height * dpr).toInt());
// Clear canvas
_gl.clearColor(0.2, 0.3, 0.3, 1.0);
_gl.clear(_gl.COLOR_BUFFER_BIT|_gl.DEPTH_BUFFER_BIT);
_gl.drawArrays(_gl.TRIANGLES, 0, 36);
print(" render n: $n ");
_gl.finish();
if (!kIsWeb) {
flutterGlPlugin.updateTexture(sourceTexture);
}
}
prepare() {
final _gl = flutterGlPlugin.gl;
String _version = "300 es";
if(!kIsWeb) {
if (Platform.isMacOS || Platform.isWindows) {
_version = "150";
}
}
var vs = """#version ${_version}
precision mediump float; // add a precision qualifier
layout (location = 0) in vec3 a_Position;
layout (location = 1) in vec2 a_TexCoord;
out vec2 TexCoord;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(a_Position, 1.0);
TexCoord = vec2(a_TexCoord.x,a_TexCoord.y);
}
""";
var fs = """#version ${_version}
precision mediump float;
out vec4 pc_fragColor;
#define gl_FragColor pc_fragColor
in vec2 TexCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;
void main() {
gl_FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}
""";
if (!initShaders(_gl, vs, fs)) {
print('Failed to intialize shaders.');
return;
}
// Write the positions of vertices to a vertex shader
n = initVertexBuffers(_gl);
if (n < 0) {
print('Failed to set the positions of the vertices');
return;
}
}
initVertexBuffers(gl) {
// Vertices
var dim = 3;
gl.enable(GL_DEPTH_TEST);
var vertices = Float32Array.fromList([
// position // texture coords
-0.5, -0.5, -0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, 0.5, -0.5, 1.0, 1.0,
0.5, 0.5, -0.5, 1.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, 0.5, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
0.5, 0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 0.0,
-0.5, 0.5, 0.5, 1.0, 0.0,
-0.5, 0.5, -0.5, 1.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 0.0,
-0.5, 0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 0.0,
0.5, 0.5, -0.5, 1.0, 1.0,
0.5, -0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 1.0,
0.5, -0.5, 0.5, 0.0, 0.0,
0.5, 0.5, 0.5, 1.0, 0.0,
-0.5, -0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 1.0, 1.0,
0.5, -0.5, 0.5, 1.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0,
-0.5, -0.5, 0.5, 0.0, 0.0,
-0.5, -0.5, -0.5, 0.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
0.5, 0.5, -0.5, 1.0, 1.0,
0.5, 0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 0.0,
-0.5, 0.5, 0.5, 0.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
]);
_vao = gl.createVertexArray();
gl.bindVertexArray(_vao);
// Create a buffer object
var vertexBuffer = gl.createBuffer();
if (vertexBuffer == null) {
print('Failed to create the buffer object');
return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
if(kIsWeb) {
gl.bufferData(gl.ARRAY_BUFFER, vertices.length, vertices, gl.STATIC_DRAW);
} else {
gl.bufferData(gl.ARRAY_BUFFER, vertices.lengthInBytes, vertices, gl.STATIC_DRAW);
}
// Assign the vertices in buffer object to a_Position variable
var a_Position = gl.getAttribLocation(glProgram, 'a_Position');
if (a_Position < 0) {
print('Failed to get the storage location of a_Position');
return -1;
}
var c_Position = gl.getAttribLocation(glProgram, 'a_TexCoord');
if(c_Position < 0){
print('Failed to get the storage location of a_TexCoord');
return -1;
}
gl.vertexAttribPointer(
a_Position, dim, gl.FLOAT, false, Float32List.bytesPerElement * 5, 0);
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(
c_Position, 2, gl.FLOAT, false, Float32List.bytesPerElement * 5, Float32List.bytesPerElement * 3);
gl.enableVertexAttribArray(c_Position);
_texture1 = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(GL_TEXTURE_2D, _texture1);
// set the texture wrapping parameters
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.uniform1i(gl.getUniformLocation(glProgram,'texture1'),0);
// load image, create texture and generate mipmaps
loadImage('assets/images/flutter540.jpg').then((bytes) {
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 540, 540, 0, GL_RGBA, GL_UNSIGNED_BYTE, Uint8Array.from(bytes.toList()));
gl.generateMipmap(GL_TEXTURE_2D);
_texture2 = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(GL_TEXTURE_2D, _texture2);
// set the texture wrapping parameters
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.uniform1i(gl.getUniformLocation(glProgram,'texture2'),1);
// load image, create texture and generate mipmaps
loadImage('assets/images/awesomeface.png').then((bytes) {
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 476, 476, 0, GL_RGBA, GL_UNSIGNED_BYTE, Uint8Array.from(bytes.toList()));
gl.generateMipmap(GL_TEXTURE_2D);
});
});
int _current = DateTime.now().millisecondsSinceEpoch;
math.Vector3 axis = math.Vector3(0.5, 1.0, 0.0);
final model = math.Matrix4.identity()..rotate(axis, _current.toDouble());
final view = math.Matrix4.identity()..translate(math.Vector3(0.0,0.0,-3.0));
math.Matrix4 projection = math.makePerspectiveMatrix(45.0,1.0, 0.1, 100.0);
math.Matrix4 MVP = projection * view * model;
var mvpPosition = gl.getUniformLocation(glProgram, 'mvp');
if(mvpPosition < 0){
print('Failed to get the storage location of MVP');
return -1;
}
gl.uniformMatrix4fv(mvpPosition, true, MVP.storage);
// Return number of vertices
return (vertices.length / dim).toInt();
}
initShaders(gl, vs_source, fs_source) {
// Compile shaders
var vertexShader = makeShader(gl, vs_source, gl.VERTEX_SHADER);
var fragmentShader = makeShader(gl, fs_source, gl.FRAGMENT_SHADER);
// Create program
glProgram = gl.createProgram();
// Attach and link shaders to the program
gl.attachShader(glProgram, vertexShader);
gl.attachShader(glProgram, fragmentShader);
gl.linkProgram(glProgram);
var _res = gl.getProgramParameter(glProgram, gl.LINK_STATUS);
print(" initShaders LINK_STATUS _res: ${_res} ");
if (_res == false || _res == 0) {
print("Unable to initialize the shader program");
return false;
}
// Use program
gl.useProgram(glProgram);
return true;
}
makeShader(gl, src, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
var _res = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (_res == 0 || _res == false) {
print("Error compiling shader: ${gl.getShaderInfoLog(shader)}");
return;
}
return shader;
}
Future loadImage(String imgPath) async{
final ByteData imageData = await rootBundle.load(imgPath);
final Uint8List bytes = imageData.buffer.asUint8List();
// Decode the image
final ui.Codec codec = await ui.instantiateImageCodec(bytes);
final ui.Image image = (await codec.getNextFrame()).image;
// Flip the image vertically
final Uint8List pixelData = await _flipImageVertically(image);
return pixelData;
}
Future _flipImageVertically(ui.Image image) async {
final int width = image.width;
final int height = image.height;
final ByteData? byteData = await image.toByteData();
final Uint8List pixels = Uint8List.fromList(byteData?.buffer.asUint8List() ?? []);
for (int y = 0; y < height ~/ 2; y++) {
final int topOffset = y * width * 4;
final int bottomOffset = (height - y - 1) * width * 4;
for (int x = 0; x < width; x++) {
final int topIndex = topOffset + x * 4;
final int bottomIndex = bottomOffset + x * 4;
final int r = pixels[topIndex];
final int g = pixels[topIndex + 1];
final int b = pixels[topIndex + 2];
final int a = pixels[topIndex + 3];
pixels[topIndex] = pixels[bottomIndex];
pixels[topIndex + 1] = pixels[bottomIndex + 1];
pixels[topIndex + 2] = pixels[bottomIndex + 2];
pixels[topIndex + 3] = pixels[bottomIndex + 3];
pixels[bottomIndex] = r;
pixels[bottomIndex + 1] = g;
pixels[bottomIndex + 2] = b;
pixels[bottomIndex + 3] = a;
}
}
return pixels;
}
}
`
When I render on Android mobile. It's not showing as a 3D cube, but as a 2D square.
My image
Can anyone help guide me?