Alternativa3DでAGALの適用とアルファブレンド

Alternativa3D(AlternativaPlatform)にて

- テクスチャマテリアルに独自のAGALを適用
- アルファ値を反映

...する解説です。


## 共通
Materialクラスを継承して `fillResources()` と `collectDraws()` をoverrideします。

- `fillResources()` で必要なGPUリソースをアップロードします。
- `collectDraws()` で描画の準備と予約をする様です。


## AGALの適用

`camera:Camera3D` から `DrawUnit` を生成し、vertexBuffer, indexBuffer, texture, shaderを準備します。

サンプルでは

- vertexShader
 - 頂点にprojectionMatrixを反映
 - uvをfragmentShaderへ渡す
- fragmentShader
 - 輝度を計算してRGBへ格納
 - アルファ値は指定の値を格納

としています。



## アルファ値の反映

fragmentShaderで `oc.w(oc.a)` にアルファ値を代入しても反映されません。
`Context3D#setBlendFactors` で反映させる旨を指定する必要があります。
- [Context3DBlendFactor - help.adobe.com]([http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display3D/Context3DBlendFactor.html)
- [Context3D#setBlendFactors - help.adobe.com](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display3D/Context3D.html#setBlendFactors\(\))

Alternativa3Dでは先の`DrawUnit`の`blendSource`と`blendDestination`に指定することで適用できます。


## サンプル
[LuminanceAndAlphaTextureMaterial.as - github](https://github.com/suzumura-ss/stage3d_utils/blob/master/src/info/smoche/alternativa/LuminanceAndAlphaTextureMaterial.as) にもあります。

```as:LuminanceAndAlphaTextureMaterial.as
package 
{
	import alternativa.engine3d.alternativa3d;
	import alternativa.engine3d.core.Camera3D;
	import alternativa.engine3d.core.DrawUnit;
	import alternativa.engine3d.core.Light3D;
	import alternativa.engine3d.core.Object3D;
	import alternativa.engine3d.core.Renderer;
	import alternativa.engine3d.core.VertexAttributes;
	import alternativa.engine3d.materials.Material;
	import alternativa.engine3d.materials.ShaderProgram;
	import alternativa.engine3d.objects.Surface;
	import alternativa.engine3d.resources.Geometry;
	import alternativa.engine3d.resources.TextureResource;
	import com.adobe.utils.AGALMiniAssembler;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DBlendFactor;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.VertexBuffer3D;
	import flash.utils.Dictionary;
	
	use namespace alternativa3d;
	/**
	 * ...
	 * @author Toshiyuki Suzumura / @suzumura_ss
	 */
	public class LuminanceAndAlphaTextureMaterial extends Material
	{
		private var _texture:TextureResource;
		private var _alpha:Number;
		private var _context3d:Context3D;
		private var _program:ShaderProgram = new ShaderProgram(null, null);
		private var _vertexShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
		private var _fragmentShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
		
		public function LuminanceAndAlphaTextureMaterial(texture:TextureResource, alpha:Number, context3d:Context3D)
		{
			_texture = texture;
			_alpha = alpha;
			_context3d = context3d;
			
			_vertexShaderAssembler.assemble(Context3DProgramType.VERTEX, [
				// op = va0[pos] * vc0[projection]
				"m44 op, va0, vc0",
				// v0 = va1[uv]
				"mov v0, va1",
			].join("\n"));
			
			_fragmentShaderAssembler.assemble(Context3DProgramType.FRAGMENT, [
				// ft0 = sampler2d(fs0, v0[uv])
				"tex ft0, v0, fs0 <2d,linear,repeat>",
				// lum = 0.299r
				"mul ft0.x, ft0.x, fc0.x",
				// lum += 0.587g
				"mul ft0.y, ft0.y, fc0.y",
				"add ft0.x, ft0.x, ft0.y",
				// lum += 0.114b
				"mul ft0.z, ft0.z, fc0.z",
				"add ft0.x, ft0.x, ft0.z",
				// g = lum, b = lum
				"mov ft0.y, ft0.x",
				"mov ft0.z, ft0.x",
				// a = <_alpha>
				"mov ft0.w, fc0.w",
				"mov oc, ft0",
			].join("\n"));
		}
		
		override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void
		{
			super.fillResources(resources, resourceType);
			
			if (_texture != null) {
				resources[_texture] = true;
			}
			_program.program = _context3d.createProgram();
			_program.program.upload(_vertexShaderAssembler.agalcode, _fragmentShaderAssembler.agalcode);
		}
		
		override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void
		{
			var object:Object3D = surface.object;
			var posBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
			var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
			var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, _program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, _program);
			
			drawUnit.setProjectionConstants(camera, 0, object.localToCameraTransform);	// = vc0
			drawUnit.setVertexBufferAt(0, posBuffer, 0, "float3");						// = va0
			drawUnit.setVertexBufferAt(1, uvBuffer,  3, "float2");						// = va1
			drawUnit.setFragmentConstantsFromNumbers(0, 0.299, 0.587, 0.114, _alpha);	// = fc0
			drawUnit.setTextureAt(0, _texture._texture);								// = fs0

			drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
			drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;

			camera.renderer.addDrawUnit(drawUnit, (objectRenderPriority >= 0)? objectRenderPriority: Renderer.OPAQUE);
		}
	}
}
```