wasabia / flutter_gl

cross-platform call OpenGL API by Dart through dart:ffi. Provides OpenGL with Texture Widget on Flutter.
243 stars 60 forks source link

Texture Problem #45

Open taipower opened 1 year ago

taipower commented 1 year ago

I write code for generate texture on rectangle. My code `import 'dart:async'; import 'dart:ffi'; import 'dart:io'; import 'dart:math'; 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/widgets.dart'; import 'package:flutter_gl/flutter_gl.dart'; import 'package:flutter_gl/openGL/opengl/opengl_es_bindings/opengl_es_bindings.dart';

typedef NativeUint8Array = Pointer;

class ExampleTexture 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 _vbo; dynamic _ebo; dynamic _texture;

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;
}

setState(() {

});

prepare();

// animate();

}

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(); } })), ], ); }

animate() { render();

// Future.delayed(Duration(milliseconds: 40), () {
//   animate();
// });

}

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;

int _current = DateTime.now().millisecondsSinceEpoch;

_gl.viewport(0, 0, (width * dpr).toInt(), (height * dpr).toInt());

num _blue = sin((_current - t) / 500);
// Clear canvas
_gl.clearColor(0.2, 0.3, 0.3, 1.0);
_gl.clear(_gl.COLOR_BUFFER_BIT);

_gl.drawElements(_gl.TRIANGLES, 6, GL_UNSIGNED_INT, 0);

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

define attribute in

define varying out

layout (location = 0) in vec3 a_Position; layout (location = 1) in vec3 a_Color; layout (location = 2) in vec2 a_TexCoord;

out vec3 ourColor; out vec2 TexCoord;

void main() { gl_Position = vec4(a_Position, 1.0); ourColor = a_Color; TexCoord = vec2(a_TexCoord.x, a_TexCoord.y); } """;

var fs = """#version ${_version}

precision mediump float;

out highp vec4 pc_fragColor;

define gl_FragColor pc_fragColor

in highp vec3 ourColor; in highp vec2 TexCoord;

uniform highp sampler2D ourTexture;

void main() { gl_FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0); } """;

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;

var vertices = Float32Array.fromList([
  // position    // colors    // texture coords
  0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // top right
  0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // bottom right
  -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // bottom left
  -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // top left
]);

var indices = Int32Array.fromList([
  0,1,3, // first triangle
  1,2,3, // second triangle
]);

_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);

_ebo = gl.createVertexArray();
gl.bindVertexArray(_ebo);

// Create a buffer object
var indicesBuffer = gl.createBuffer();
if (indicesBuffer == null) {
  print('Failed to create the buffer object');
  return -1;
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);

if(kIsWeb) {
  gl.bufferData(gl.ARRAY_BUFFER, vertices.length, vertices, gl.STATIC_DRAW);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices.length, indices, gl.STATIC_DRAW);
} else {
  gl.bufferData(gl.ARRAY_BUFFER, vertices.lengthInBytes, vertices, gl.STATIC_DRAW);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices.lengthInBytes, indices, 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 b_Position = gl.getAttribLocation(glProgram, 'a_Color');
if(b_Position < 0){
  print('Failed to get the storage location of a_Color');
  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 * 8, 0);
gl.enableVertexAttribArray(a_Position);

gl.vertexAttribPointer(
    b_Position, dim, gl.FLOAT, false, Float32List.bytesPerElement * 8, Float32List.bytesPerElement * 3);
gl.enableVertexAttribArray(b_Position);

gl.vertexAttribPointer(
  c_Position, 2, gl.FLOAT, false, Float32List.bytesPerElement * 8, Float32List.bytesPerElement * 6);
gl.enableVertexAttribArray(c_Position);

_texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(GL_TEXTURE_2D, _texture);
// 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);

// load image, create texture and generate mipmaps
loadImage('assets/images/flutter.jpg').then((bytes) {
  final List<int> dataList = bytes.toList();
  Uint8Array data = Uint8Array.from(dataList);
  gl.texImage2D(GL_TEXTURE_2D, 0, gl.RGB, 80, 80, 0, gl.RGB, GL_UNSIGNED_BYTE, data);
  gl.generateMipmap(GL_TEXTURE_2D);
});

// 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{ ByteData byteData = await rootBundle.load(imgPath); Uint8List bytes = byteData.buffer.asUint8List();

return bytes;

}

}`
When I run Screenshot_20230320_163430 My image flutter

taipower commented 1 year ago

Can anyone help guide me?

wasabia commented 1 year ago

Texture need image pixels not file bytes?

taipower commented 1 year ago

@wasabia I think that convert image to Unit8Array. I updated my code in convert image to Unit8Array // load image, create texture and generate mipmaps loadImage('assets/images/thumbnail2.jpg').then((bytes) { gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1080, 1080, 0, GL_RGB, GL_UNSIGNED_BYTE, Uint8Array.from(bytes.toList())); gl.generateMipmap(GL_TEXTURE_2D); });

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;

}

But I found problem. Image color is grey only and disproportionate. Screenshot_20230322_202423 My image thumbnail2

taipower commented 1 year ago

Hi @wasabia I can solve the problem now. Thank you for your response. Screenshot_20230323_134703

ctp27 commented 2 months ago

@taipower how did you solve it? Can you please share your code?