Environment Reflect Refract

Posted by davidejones

I’ve recently been playing with environment reflection and refraction using a cube map after reading the information in nvida gpu gems environment mapping techniques. The thing that attracted me most to this was the variation in light and color from the chromatic dispersion just like you would see in real life situations. There are a few nice examples online that all follow the same principles. I used these to help me achieve what I wanted with alternativa3d. So a big thank you to gonchar.me and the webgl example glass skull which you can clearly see my code is based on. Also a thank you to Jockum Skoglund who is the creator of the cubemap.

Here is my shader

package alternativa.engine3d.materials 
	import alternativa.engine3d.core.Camera3D;
	import alternativa.engine3d.core.DebugMaterialsRenderer;
	import alternativa.engine3d.core.DrawUnit;
	import alternativa.engine3d.core.Light3D;
	import alternativa.engine3d.core.Object3D;
	import alternativa.engine3d.core.Transform3D;
	import alternativa.engine3d.objects.Surface;
	import alternativa.engine3d.resources.BitmapCubeTextureResource;
	import alternativa.engine3d.resources.Geometry;
	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.resources.TextureResource;
	import flash.display3D.textures.Texture;
	import flash.geom.Vector3D;
	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;
	import alternativa.engine3d.alternativa3d;
	use namespace alternativa3d;
	 * ...
	 * @author David E Jones
	public class RefractMaterial3 extends Material 
		private static var caches:Dictionary = new Dictionary(true);
		private var cachedContext3D:Context3D;
		private var programsCache:Dictionary;
		static alternativa3d const getDiffuseVProcedure:Procedure = new Procedure([
			//vertex normal
			"mov t0, a1",
			//normal from local to global
			"m33 t0.xyz, t0.xyz, c5",
			"sub t1, c4, a0",
			//viewVec from local to global
			"m33 t1.xyz, t1.xyz, c5",	
			"nrm t0.xyz, t0",
			"nrm t1.xyz, t1",
			//pass to frag
			"mov v0, t0",
			"mov v1, t1",			
		], "getDiffuseVProcedure");
		// Fragment procedure
		static alternativa3d const getDiffuseFProcedure:Procedure = new Procedure([
			"dp3 t0, v1, v0",
			"add t0, t0, t0",
			"mul t0, v0, t0",
			"sub t0, v1, t0",
			"neg t0, t0",
			"nrm t0.xyz, t0",
			"tex t0, t0, s0 ",
			//refract R
			"dp3 t2, v1, v0",
			"mul t2, t2, v0",
			"sub t2, t2, v1",
			"mul t2, t2, c0.x",
			"dp3 t1, v1, v0",
			"mul t1, t1, t1",
			"sub t1, c0.w, t1",
			"mul t1, c1.x, t1", 
			"sub t1, c0.w, t1",
			"sqt t1, t1",
			"mul t1, t1, v0",
			"sub t1, t2, t1",
			//"nrm t1.xyz, t1",
			"tex t3, t1, s0 ",
			//refract G
			"dp3 t2, v1, v0",
			"mul t2, t2, v0",
			"sub t2, t2, v1",
			"mul t2, t2, c0.y",
			"dp3 t1, v1, v0",
			"mul t1, t1, t1",
			"sub t1, c0.w, t1",
			"mul t1, c1.y, t1", 
			"sub t1, c0.w, t1",
			"sqt t1, t1",
			"mul t1, t1, v0",
			"sub t1, t2, t1",
			//"nrm t1.xyz, t1",
			"tex t4, t1, s0 ",
			//refract B
			"dp3 t2, v1, v0",
			"mul t2, t2, v0",
			"sub t2, t2, v1",
			"mul t2, t2, c0.z",
			"dp3 t1, v1, v0",
			"mul t1, t1, t1",
			"sub t1, c0.w, t1",
			"mul t1, c1.z, t1", 
			"sub t1, c0.w, t1",
			"sqt t1, t1",
			"mul t1, t1, v0",
			"sub t1, t2, t1",
			//"nrm t1.xyz, t1",
			"tex t5, t1, s0 ",
			//combine rgb
			"mov t1.x, t3.x",
			"mov t1.y, t4.y",
			"mov t1.z, t5.z",
			"mov t1.w, c0.w",
			//rfac = bias + scale * pow(1.0 + dot(incident, vNormal), power);
			"dp3 t6, v1, v0", //dot(incident, vNormal)
			"add t6, t6, c2.w", //(1.0 + dot(incident, vNormal)
			"pow t6, t6, c2.z", //pow()
			"mul t6, t6, c2.y", //scale * pow()
			"add t6, t6, c2.x", // + bias		
			//gl_FragColor = ret * rfac + ref * (1.0 - rfac);
			"sub t4, c0.w, t6", //(1.0 - rfac)
			"mul t4, t0, t4", //ref * (1.0 - rfac)
			"mul t5, t1, t6", //ret * rfac
			"add t4, t4, t5", // add both together
			//"sub t2, t0, t1",
			//"mul t2, t2, v2",
			//"add t2, t2, t1",
			//output color
			"mov o0, t4"
		], "getDiffuseFProcedure");
		public var alphaThreshold:Number = 0;
		public var alpha : Number = 1;
		public var chromaticDispersion:Vector3D = new Vector3D(0.9, 0.97, 1.04, 1);
		public var fresnelBias:Number = 0.9;
		public var fresnelScale:Number = 0.7;
		public var fresnelPower:Number = 1.1;
		static alternativa3d const _passUVProcedure:Procedure = new Procedure(["#v0=vUV", "#a0=aUV", "mov v0, a0"], "passUVProcedure");
		static alternativa3d const _passNormProcedure:Procedure = new Procedure(["#v0=vNormal", "#a1=aNormal", "mov v0, a1"], "passNormProcedure");
		private var localToGlobalTransform:Transform3D = new Transform3D();
		private var cubetexture:TextureResource;
		public function RefractMaterial3(cubetexture:TextureResource) 
			this.cubetexture = cubetexture;
		override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
			super.fillResources(resources, resourceType);
			if (cubetexture != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(cubetexture)) as Class, resourceType)) {
				resources[cubetexture] = true;
		private function getProgram(object:Object3D, programs:Vector., camera:Camera3D, alphaTest:int):DiffuseMaterialProgram {
			var key:int = 0;
			var program:DiffuseMaterialProgram = programs[key];
			if (program == null) {
				var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
				var positionVar:String = "aPosition";
				var normalVar:String = "aNormal";
				vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
				vertexLinker.declareVariable(normalVar, VariableType.ATTRIBUTE);
				if (object.transformProcedure != null) {
					positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
				vertexLinker.setInputParams(_projectProcedure, positionVar);
				// Pixel shader
				var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
				var outProcedure:Procedure = getDiffuseFProcedure;
				if (alphaTest > 0) {
					fragmentLinker.setOutputParams(outProcedure, "tColor");
					if (alphaTest == 1) {
						fragmentLinker.addProcedure(thresholdOpaqueAlphaProcedure, "tColor");
					} else {
						fragmentLinker.addProcedure(thresholdTransparentAlphaProcedure, "tColor");
				fragmentLinker.varyings = vertexLinker.varyings;

				program = new DiffuseMaterialProgram(vertexLinker, fragmentLinker);

				programs[key] = program;
			return program;
		private function getDrawUnit(program:DiffuseMaterialProgram, camera:Camera3D, surface:Surface, geometry:Geometry):DrawUnit {
			var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
			var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
			var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);

			var object:Object3D = surface.object;

			// Draw call
			var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);

			// Streams
			// a0, a1
			drawUnit.setVertexBufferAt(program.aPosition, positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
			drawUnit.setVertexBufferAt(program.aNormal, normalsBuffer, geometry._attributesOffsets[VertexAttributes.NORMAL], VertexAttributes.FORMATS[VertexAttributes.NORMAL]);
			//drawUnit.setVertexBufferAt(program.aNormal, uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]);
			object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
			drawUnit.setProjectionConstants(camera, program.cProjMatrix, object.localToCameraTransform); //cProjMatrix c0;
			//local-space camera position
			var cameraToLocalTransform : Transform3D = object.cameraToLocalTransform;
			drawUnit.setVertexConstantsFromNumbers(program.cLocalCamera, cameraToLocalTransform.d, cameraToLocalTransform.h, cameraToLocalTransform.l);
			//calculating local to global transform
			localToGlobalTransform.combine(camera.localToGlobalTransform, object.localToCameraTransform);
			drawUnit.setVertexConstantsFromTransform(program.cGlobalTransform, localToGlobalTransform);

			drawUnit.setTextureAt(program.sCubeMap, cubetexture._texture);
			drawUnit.setFragmentConstantsFromNumbers(program.cChromaticDispersion, chromaticDispersion.x, chromaticDispersion.y, chromaticDispersion.z, 1);
			drawUnit.setFragmentConstantsFromNumbers(program.cChromaticDispersionSquared, chromaticDispersion.x * chromaticDispersion.x, chromaticDispersion.y * chromaticDispersion.y, chromaticDispersion.z * chromaticDispersion.z, 1);
			drawUnit.setFragmentConstantsFromNumbers(program.cFresnel, fresnelBias, fresnelScale, fresnelPower, 1);
			drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
			drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;

			return drawUnit;
		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;
			var lightGroup:Vector. = new Vector.();
			var light:Light3D;

			// Buffers
			var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
			var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);

			// Check validity
			if (positionBuffer == null || normalsBuffer == 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.(5, true);
				programsCache[object.transformProcedure] = optionsPrograms;
			var program:DiffuseMaterialProgram;
			var drawUnit:DrawUnit;
			if (alphaThreshold > 0) {
				program = getProgram(object, optionsPrograms, camera, 1);
				drawUnit = getDrawUnit(program, camera, surface, geometry);
			} else {
				program = getProgram(object, optionsPrograms, camera, 0);
				drawUnit = getDrawUnit(program, camera, surface, geometry);
			// Use z-buffer within DrawCall, draws without blending
			camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
			//camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);


import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;

import flash.display3D.Context3D;

class DiffuseMaterialProgram extends ShaderProgram {

	public var aPosition:int = -1;
	public var aNormal:int = -1;
	public var cProjMatrix:int = -1;	
	public var cLocalCamera:int = -1;	
	public var cGlobalTransform:int = -1;
	public var cTempV:int = -1;
	public var sCubeMap:int = -1;
	public var cChromaticDispersion:int = -1;
	public var cChromaticDispersionSquared:int = -1;
	public var cFresnel:int = -1;

	public function DiffuseMaterialProgram(vertex:Linker, fragment:Linker) {
		super(vertex, fragment);

	override public function upload(context3D:Context3D):void {

		aPosition = vertexShader.findVariable("aPosition");
		aNormal = vertexShader.findVariable("aNormal");
		cProjMatrix = vertexShader.findVariable("cProjMatrix");
		cLocalCamera = vertexShader.findVariable("cLocalCamera");
		cGlobalTransform = vertexShader.findVariable("cGlobalTransform");
		cTempV = vertexShader.findVariable("cTempV");
		sCubeMap = fragmentShader.findVariable("sCubeMap");
		cChromaticDispersion = fragmentShader.findVariable("cChromaticDispersion");
		cChromaticDispersionSquared = fragmentShader.findVariable("cChromaticDispersionSquared");
		cFresnel = fragmentShader.findVariable("cFresnel");


and its usage

var envMap:BitmapCubeTextureResource = new BitmapCubeTextureResource(
	new EmbedLeft().bitmapData, 
	new EmbedRight().bitmapData,
	new EmbedBack().bitmapData,
	new EmbedFront().bitmapData,
	new EmbedBottom().bitmapData,
	new EmbedTop().bitmapData,

var mat = new RefractMaterial3(envMap);


There is no full download with this blog post, i’m in the process of tidying up this code as its still a little messy. The above code is completely usable though so give it a try!

Trackback URL for this post: http://davidejones.com/blog/1692-environment-reflect-refract/trackback/

Being Sociable...

  • If you like this article then please share it on your favourite social network and follow me on twitter for the latest updates

5 Responses to Environment Reflect Refract

  • Pingback: [Stage3D] CubeTexture verwenden - Flashforum

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>