忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

dartで3DCGのピクセルデータをCPUで計算する方法

Dartでレンダーターゲットのピクセルを読む

以前、TypeScriptでレンダーターゲットのピクセルを読んだことがありました。
描画した3DCGのピクセルを読み込みます。

今回はGoogleの言語、Dartでレンダーターゲットのピクセルを読もうと思います。


Three.dartではピクセルを読めない

ところが、Dartの3DCGライブラリThree.dartはまだ発展途上なのか、レンダーターゲットからピクセルを読めません。
しかたがないので一部WebGLを直接使います。

デモプログラム

pixel count

コード

import 'dart:html';
import 'dart:math';
import 'dart:web_gl';
import 'dart:typed_data';
import 'package:three/three.dart';

WebGLRenderer renderer;
Scene scene = new Scene();
PerspectiveCamera camera = new PerspectiveCamera();
Mesh cube;
Framebuffer renderTargetFrameBuffer;
Uint8List pixels;

CanvasElement canvas;

void main(){
  canvas = querySelector("#myCanvas");
  
  camera.fov = PI / 3;
  camera.aspect = canvas.width / canvas.height;
  camera.near = 0.1;
  camera.far = 1000.0;
  camera.translateZ(3.0);
  
  renderer = new WebGLRenderer(canvas:canvas);
  renderer.setClearColor(new Color(0x000000), 1.0);
  
  var geometry = new CubeGeometry(1.0, 1.0, 1.0);
  var material = new MeshBasicMaterial(color:0x00ff00);
  cube = new Mesh(geometry, material);
  scene.add(cube);
  
  renderTargetFrameBuffer = createRenderTarget();
  
  pixels = new Uint8List(canvas.width * canvas.height * 4);
  
  _animate(0.0);
}

Framebuffer createRenderTarget() {
  //Three.dartではレンダーターゲットからピクセルを読み込めないようです。
  //なのでWebGLを直接使います。
  RenderingContext gl = renderer.context;
  
  final int renderTargetWidth = canvas.width;
  final int renderTargetHeight = canvas.height;
  var renderTargetFrameBuffer = gl.createFramebuffer();
  gl.bindFramebuffer(FRAMEBUFFER, renderTargetFrameBuffer);
  
  print("renderTargetTexture");
  var renderTargetTexture = gl.createTexture();
  print("bindTexture");
  gl.bindTexture(TEXTURE_2D, renderTargetTexture);
  print("texImage2D");
  gl.texImage2D(
      TEXTURE_2D, 
      0,
      RGBA,
      renderTargetWidth, 
      renderTargetHeight, 
      0, 
      RGBA, 
      UNSIGNED_BYTE, 
      new Uint8List(renderTargetWidth * renderTargetHeight * 4));
  
  print("renderBuffer");
  var renderBuffer = gl.createRenderbuffer();
  gl.bindRenderbuffer(RENDERBUFFER, renderBuffer);
  gl.renderbufferStorage(
      RENDERBUFFER, 
      DEPTH_COMPONENT16, 
      renderTargetWidth, 
      renderTargetHeight);
  
  gl.framebufferTexture2D(
      FRAMEBUFFER, 
      COLOR_ATTACHMENT0, 
      TEXTURE_2D, 
      renderTargetTexture, 
      0);
  gl.framebufferRenderbuffer(
      FRAMEBUFFER, 
      DEPTH_ATTACHMENT, 
      RENDERBUFFER, 
      renderBuffer);
  
  print("set null");
  gl.bindTexture(TEXTURE_2D, null);
  gl.bindRenderbuffer(RENDERBUFFER, null);
  gl.bindFramebuffer(FRAMEBUFFER, null);
  
  return renderTargetFrameBuffer;
}


void _animate(double deltaTime){
  cube.rotation.y += 0.02;
  
  renderRenderTarget();
  
  showResults();
  
  renderer.render(scene, camera);
  
  window.animationFrame.then(_animate);
}

void renderRenderTarget() {
  RenderingContext gl = renderer.context;
  
  gl.bindFramebuffer(FRAMEBUFFER, renderTargetFrameBuffer);
  renderer.render(scene, camera);
  gl.readPixels(
      0,
      0, 
      canvas.width, 
      canvas.height, 
      RGBA,
      UNSIGNED_BYTE,
      pixels);
  gl.bindFramebuffer(FRAMEBUFFER, null);
}

void showResults(){
  int black = 0;
  int green = 0;
  
  for(int y = 0; y < canvas.height;y++){
    for(int x = 0; x < canvas.width; x++){
      int index = (x + canvas.width * y) * 4;
      if(pixels[index] == 0 && pixels[index + 1] == 0 && pixels[index + 2] == 0){
        black++;
      }
      else if(pixels[index] == 0 && pixels[index + 1] == 0xFF && pixels[index + 2] == 0){
        green++;
      }
    }
  }
  
  querySelector("#pixelCountView").text 
    = "black: $black, green: $green";
}

以上です。
早くthree.dartでも読めるようになるといいですね。

拍手[0回]

PR