Texture Projection Material Alternativa3d
I’ve been experimenting some more with the alternativa3d materials in the hope to create some interesting demos. I’ve been trying to help out in the creation of a reflection plane and water type materials. In my experiments I’ve created a type of texture projection material check it out. Here is the material
package alternativa.engine3d.materials {
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.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.Geometry;
import alternativa.engine3d.resources.TextureResource;
import avmplus.getQualifiedClassName;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.VertexBuffer3D;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
use namespace alternativa3d;
public class TextureProjectionMaterial extends Material {
private static var caches:Dictionary = new Dictionary(true);
private var cachedContext3D:Context3D;
private var programsCache:Dictionary;
// Vertex Procedure
static alternativa3d const getProjectionVProcedure:Procedure = new Procedure([
"#a0=aPosition",
"m44 t0.xyzw a0.xyzw c0.xyzw",
"mov v0.xyzw t0.xyzw",
"mov o0.xyzw t0.xyzw"
], "getProjectionVProcedure");
// Fragment procedure
static alternativa3d const getProjectionSProcedure:Procedure = new Procedure([
"#c0=cAmount",
"#c1=cThresholdAlpha",
"#s0=sDiffuse",
"div t0.xy v0.xyzw v0.wwxw",
"mul t0.zw t0.xxxy c0.xxxy",
"add t0.xy t0.zwzw c0.zzzz",
"tex t1,t0.xyzw,s0 <2d,repeat,linear>",
"mul t1.w, t1.w, c1.w",
"mov o0, t1"
], "getProjectionSProcedure");
static alternativa3d const getProjectionOpacitySProcedure:Procedure = new Procedure([
"#c0=cAmount",
"#c1=cThresholdAlpha",
"#s0=sDiffuse",
"#s1=sOpacity",
"div t0.xy v0.xyzw v0.wwxw",
"mul t0.zw t0.xxxy c0.xxxy",
"add t0.xy t0.zwzw c0.zzzz",
"tex t1, t0.xyzw, s0 <2d, linear,repeat, miplinear>",
"tex t2, t0.xyzw, s1 <2d, linear,repeat, miplinear>",
"mul t1.w, t2.x, c1.w",
"mov o0, t1"
], "getProjectionOpacitySProcedure");
static alternativa3d const thresholdOpaqueAlphaProcedure:Procedure = new Procedure([
"#c0=cThresholdAlpha",
"sub t0.w, i0.w, c0.x",
"kil t0.w",
"mov o0, i0"
], "thresholdOpaqueAlphaProcedure");
static alternativa3d const thresholdTransparentAlphaProcedure:Procedure = new Procedure([
"#c0=cThresholdAlpha",
"slt t0.w, i0.w, c0.x",
"mul i0.w, t0.w, i0.w",
"mov o0, i0"
], "thresholdTransparentAlphaProcedure");
public var diffuseMap:TextureResource;
public var opacityMap:TextureResource;
public var transparentPass:Boolean = true;
public var opaquePass:Boolean = true;
public var alphaThreshold:Number = 0;
public var alpha:Number = 1;
/**
* Creates a new TextureMaterial instance.
*
* @param diffuseMap Diffuse map.
* @param alpha Transparency.
*/
public function TextureProjectionMaterial(diffuseMap:TextureResource = null, opacityMap:TextureResource = null, alpha:Number = 1) {
this.diffuseMap = diffuseMap;
this.opacityMap = opacityMap;
this.alpha = alpha;
}
/**
* @private
*/
override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
super.fillResources(resources, resourceType);
if (diffuseMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(diffuseMap)) as Class, resourceType)) {
resources[diffuseMap] = true;
}
if (opacityMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(opacityMap)) as Class, resourceType)) {
resources[opacityMap] = true;
}
}
/**
* @param object
* @param programs
* @param camera
* @param opacityMap
* @param alphaTest 0 - disabled, 1 - opaque, 2 - contours
* @return
*/
private function getProgram(object:Object3D, programs:Vector., camera:Camera3D, opacityMap:TextureResource, alphaTest:int):TextureProjectionMaterialProgram {
var key:int = (opacityMap != null ? 3 : 0) + alphaTest;
var program:TextureProjectionMaterialProgram = programs[key];
if (program == null) {
// Make program
// Vertex shader
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var positionVar:String = "aPosition";
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
getProjectionVProcedure.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4);
if (object.transformProcedure != null) {
positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
}
vertexLinker.addProcedure(getProjectionVProcedure);
vertexLinker.setInputParams(getProjectionVProcedure, positionVar);
// Pixel shader
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
var outProcedure:Procedure = (opacityMap != null ? getProjectionOpacitySProcedure : getProjectionSProcedure);
fragmentLinker.addProcedure(outProcedure);
if (alphaTest > 0) {
fragmentLinker.declareVariable("tColor");
fragmentLinker.setOutputParams(outProcedure, "tColor");
if (alphaTest == 1) {
fragmentLinker.addProcedure(thresholdOpaqueAlphaProcedure, "tColor");
} else {
fragmentLinker.addProcedure(thresholdTransparentAlphaProcedure, "tColor");
}
}
fragmentLinker.varyings = vertexLinker.varyings;
program = new TextureProjectionMaterialProgram(vertexLinker, fragmentLinker);
program.upload(camera.context3D);
programs[key] = program;
}
return program;
}
private function getDrawUnit(program:TextureProjectionMaterialProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource):DrawUnit {
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
var object:Object3D = surface.object;
// Draw call
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
// Streams
drawUnit.setVertexBufferAt(program.aPosition, positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
//Constants
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
drawUnit.setProjectionConstants(camera, program.cProjMatrix, object.localToCameraTransform);
drawUnit.setFragmentConstantsFromNumbers(program.cAmount, 0.5, 0.5, 0.5, 0.5);
drawUnit.setFragmentConstantsFromNumbers(program.cThresholdAlpha, alphaThreshold, 0, 0, alpha);
// Textures
drawUnit.setTextureAt(program.sDiffuse, diffuseMap._texture);
if (opacityMap != null) {
drawUnit.setTextureAt(program.sOpacity, opacityMap._texture);
}
return drawUnit;
}
/**
* @private
*/
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void {
var object:Object3D = surface.object;
// Buffers
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
// Check validity
if (positionBuffer == null || uvBuffer == null || diffuseMap == null || diffuseMap._texture == null || opacityMap != null && opacityMap._texture == null) return;
// Refresh program cache for this context
if (camera.context3D != cachedContext3D) {
cachedContext3D = camera.context3D;
programsCache = caches[cachedContext3D];
if (programsCache == null) {
programsCache = new Dictionary();
caches[cachedContext3D] = programsCache;
}
}
var optionsPrograms:Vector. = programsCache[object.transformProcedure];
if(optionsPrograms == null) {
optionsPrograms = new Vector.(6, true);
programsCache[object.transformProcedure] = optionsPrograms;
}
var program:TextureProjectionMaterialProgram;
var drawUnit:DrawUnit;
// Opaque pass
if (opaquePass && alphaThreshold <= alpha) {
if (alphaThreshold > 0) {
// Alpha test
// use opacityMap if it is presented
program = getProgram(object, optionsPrograms, camera, opacityMap, 1);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
} else {
// do not use opacityMap at all
program = getProgram(object, optionsPrograms, camera, null, 0);
drawUnit = getDrawUnit(program, camera, surface, geometry, null);
}
// Use z-buffer within DrawCall, draws without blending
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
}
// Transparent pass
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
// use opacityMap if it is presented
if (alphaThreshold <= alpha && !opaquePass) {
// Alpha threshold
program = getProgram(object, optionsPrograms, camera, opacityMap, 2);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
} else {
// There is no Alpha threshold or check z-buffer by previous pass
program = getProgram(object, optionsPrograms, camera, opacityMap, 0);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
}
// Do not use z-buffer, draws with blending
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
}
}
/**
* @inheritDoc
*/
override public function clone():Material {
var res:TextureProjectionMaterial = new TextureProjectionMaterial(diffuseMap, opacityMap, alpha);
res.clonePropertiesFrom(this);
return res;
}
/**
* @inheritDoc
*/
override protected function clonePropertiesFrom(source:Material):void {
super.clonePropertiesFrom(source);
var tex:TextureProjectionMaterial = source as TextureProjectionMaterial;
diffuseMap = tex.diffuseMap;
opacityMap = tex.opacityMap;
opaquePass = tex.opaquePass;
transparentPass = tex.transparentPass;
alphaThreshold = tex.alphaThreshold;
alpha = tex.alpha;
}
}
}
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;
import flash.display3D.Context3D;
class TextureProjectionMaterialProgram extends ShaderProgram {
public var aPosition:int = -1;
public var aUV:int = -1;
public var cProjMatrix:int = -1;
public var cThresholdAlpha:int = -1;
public var cAmount:int = -1;
public var sDiffuse:int = -1;
public var sOpacity:int = -1;
public function TextureProjectionMaterialProgram(vertex:Linker, fragment:Linker) {
super(vertex, fragment);
}
override public function upload(context3D:Context3D):void {
super.upload(context3D);
aPosition = vertexShader.findVariable("aPosition");
aUV = vertexShader.findVariable("aUV");
cProjMatrix = vertexShader.findVariable("cProjMatrix");
cThresholdAlpha = fragmentShader.findVariable("cThresholdAlpha");
cAmount = fragmentShader.findVariable("cAmount");
sDiffuse = fragmentShader.findVariable("sDiffuse");
sOpacity = fragmentShader.findVariable("sOpacity");
}
}
and the usage is just like any other material in this case just pass in the texture and that’s it.
b = new Box();
var m:TextureProjectionMaterial = new TextureProjectionMaterial(new BitmapTextureResource(new IMG().bitmapData));
b.setMaterialToAllSurfaces(m);
scene.addChild(b);
uploadResources(b.getResources(true));
Hello David,
Your demos is great! Thank you!