Environment Reflect Refract

David Jones

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. environment reflection and refraction 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!


  • avatar-mmmaxxx
    # MMMaXXX
    Hi. David! Good work! You dealt with Linker, and I here didn’t master.
    • avatar-davidejones
      # davidejones
      Thank you! Took me a long time to do because of trial and error but i’m happy now.
      • avatar-mmmaxxx
        # MMMaXXX
        You tried to add something in StandardtMaterial. To use together reflections and light?
        • avatar-davidejones
          # davidejones
          no i haven’t. I might do one day but not anytime soon
  • avatar-uv-scrolling-material-for-alternativa3d
    # UV Scrolling Material for Alternativa3D |
    […] einmal so ein Shader geschrieben und kann evtl. helfen? Was ich bisher habe ist von dieser Seite: Alternativa3d Environment Reflect Refract Chromatic Dispersion | David E Jones a web developer with … Und mein VertexShader sieht so aus: […]

Comments are currently closed