(en) Environment and Refraction in Alternativa3D



In this lesson I’ll show you how to create your own environment and refract material in Alternativa3D 8.17.0.

Cube Texture

Molehill supports cube mapping. Cube mapping is a method of environment mapping that uses a six-sided cube as the map shape. The environment is projected onto the six faces of a cube and stored as six square textures, or unfolded into six regions of a single texture. For create new cube-map you need to call createCubeTexture method of created instance of Context3D. For upload each side of cube in cubemap you need to use uploadFromBitmapData method of CubeTexture instance. This method uploads each mip-level.

Environment


For calculating Environment I need to know: vertex normal, object local-space camera position, object local to global matrix. Environment vector calculated by this formula:

V – View Vector = camera position – vertex position, N – Normal

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.RenderPriority;
    import alternativa.engine3d.core.Transform3D;
   import alternativa.engine3d.core.VertexAttributes;
  import alternativa.engine3d.materials.A3DUtils;
 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 avmplus.getQualifiedClassName;
 
   import com.adobe.utils.AGALMiniAssembler;
 
   import flash.display3D.Context3DProgramType;
    import flash.display3D.VertexBuffer3D;
  import flash.utils.Dictionary;
  import flash.utils.getDefinitionByName;
 
 use namespace alternativa3d;
    public class MyGlassMaterial extends Material {
     alternativa3d var _cubeTexture : TextureResource;
       //vertex shader
     private var _vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
       //fragment shader
       private var _fragmentShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
     private var program : ShaderProgram;
 
        public function MyGlassMaterial(cubeTexture : TextureResource = null) {
         _cubeTexture = cubeTexture;
         program = new ShaderProgram(null, null);
            _vertexShaderAssembler.assemble(Context3DProgramType.VERTEX,
            //vertex normal
         "mov vt0, va1\n"+
          //normal from local to global
           "m33 vt0.xyz, vt0.xyz, vc5\n"+
         //viewVec
           "sub vt1, vc4, va0\n"+
         //viewVec from local to global
          "m33 vt1.xyz, vt1.xyz, vc5\n"+
         // r = V - 2(V.N)*N
         "dp3 vt2, vt1, vt0\n"+
         "add vt2, vt2, vt2\n"+
         "mul vt2, vt0, vt2\n"+
         "sub vt2, vt1, vt2\n"+
         "neg vt2, vt2\n"+
          "nrm vt2.xyz, vt2.xyz\n"+
          //send to fragment shader
           "mov v0, vt2\n"+
           //project vertex
            "m44 op, va0, vc0");
 
            _fragmentShaderAssembler.assemble(Context3DProgramType.FRAGMENT,
            //get cubemap color
         "tex ft0,v0.xyz,fs0 \n"+
           //output color
          "mov oc, ft0"
           );
      }
       alternativa3d override function fillResources(resources : Dictionary, resourceType : Class) : void {
            //fill cubeTexture
          if (_cubeTexture != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_cubeTexture)) as Class, resourceType)) {
                resources[_cubeTexture] = true;
         }
           //upload program
            program.program = Main.instance.stage.stage3Ds[0].context3D.createProgram();
            program.program.upload(_vertexShaderAssembler.agalcode, _fragmentShaderAssembler.agalcode);
     }
 
       //draw each surface of object
       override alternativa3d function collectDraws(camera : Camera3D, surface : Surface, geometry : Geometry, lights : Vector., lightsLength : int, objectRenderPriority : int = -1) : void {
         //get link to object
            var object : Object3D = surface.object;
         //positionBuffer 
           var positionBuffer : VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
          //normalsBuffer 
            var normalsBuffer : VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
 
         var drawUnit : DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
            //setting buffers
           //point types float3,float3 and offsets 0,5
         drawUnit.setVertexBufferAt(0, positionBuffer, 0, "float3");
         drawUnit.setVertexBufferAt(1, normalsBuffer, 5, "float3");
          //project matrix
            drawUnit.setProjectionConstants(camera, 0, object.localToCameraTransform);
          //local-space camera position
           var cameraToLocalTransform : Transform3D = object.cameraToLocalTransform;
           drawUnit.setVertexConstantsFromNumbers(4, cameraToLocalTransform.d, cameraToLocalTransform.h, cameraToLocalTransform.l);
            //calculating local to global transform
         var globalTransform : Transform3D = new Transform3D();
          globalTransform.copy(object.localToCameraTransform);
            globalTransform.append(camera.localToGlobalTransform);
          //send this transform
           drawUnit.setVertexConstantsFromTransform(5, globalTransform);
           //set cubemap texture       
            drawUnit.setTextureAt(0, _cubeTexture._texture);
            //add surface to draw
           camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority:RenderPriority.OPAQUE);
       }
   }
}

Refraction

For refraction ray I used Shell’s law.
Source formula:


Changing n1/n2 by Refraction Index we calculate next formula:

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.RenderPriority;
    import alternativa.engine3d.core.Transform3D;
   import alternativa.engine3d.core.VertexAttributes;
  import alternativa.engine3d.materials.A3DUtils;
 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 avmplus.getQualifiedClassName;
 
   import com.adobe.utils.AGALMiniAssembler;
 
   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 MyGlassMaterial extends Material {
     alternativa3d var _cubeTexture : TextureResource;
       //vertex shader
     private var _vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
       //fragment shader
       private var _fragmentShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
     private var program : ShaderProgram;
        public var reflectionPower: Number = 0.4;
       public var refractionIndex : Number = 0.01;
     public var alpha : Number = 0.8;
 
        public function MyGlassMaterial(cubeTexture : TextureResource = null) {
         _cubeTexture = cubeTexture;
         program = new ShaderProgram(null, null);
            _vertexShaderAssembler.assemble(Context3DProgramType.VERTEX,
            //vertex normal
         "mov vt0, va1\n"+
          //normal from local to global
           "m33 vt0.xyz, vt0.xyz, vc5\n"+
         //viewVec
           "sub vt1, vc4, va0\n"+
         //viewVec from local to global
          "m33 vt1.xyz, vt1.xyz, vc5\n"+
         // r = V - 2(V.N)*N
         "dp3 vt2, vt1, vt0\n"+
         "add vt2, vt2, vt2\n"+
         "mul vt2, vt0, vt2\n"+
         "sub vt2, vt1, vt2\n"+
         "neg vt2, vt2\n"+
          "nrm vt2.xyz, vt2.xyz\n"+
          //send to fragment shader
           "mov v0, vt2\n"+
           //REFR_IND*(V.N)*N-V) - SQRT(1-REFR_IND^2(1-(V.N)^2))*N
         //(V.N)
         "dp3 vt3, vt1, vt0\n"+
         //(V.N)*N
           "mul vt3, vt3, vt0\n"+
         //(V.N)*N-V
         "sub vt3, vt3, vt1\n"+
         //REFR_IND*(V.N)*N-V)
           "mul vt3, vt3, vc5.y\n"+
           //V.N
           "dp3 vt4, vt1, vt0\n"+
         //(V.N)^2
           "mul vt4, vt4, vt4\n"+
         //(1-(V.N)^2)
           "sub vt4, vc5.x, vt4\n"+
           //REFR_IND^2(1-(V.N)^2)
         "mul vt4, vc5.z, vt4\n"+
           //1-REFR_IND^2(1-(V.N)^2)
           "sub vt4, vc5.x, vt4\n"+
           //SQRT(1-REFR_IND^2(1-(V.N)^2))
         "sqt vt4, vt4\n"+
          //SQRT(1-REFR_IND^2(1-(V.N)^2))*N
           "mul vt4, vt4, vt0\n"+
         //REFR_IND*(V.N)*N-V) - SQRT(1-REFR_IND^2(1-(V.N)^2))*N
         "sub vt4, vt3, vt4\n"+
         //normalize
         "nrm vt4.xyz, vt4.xyz\n"+
          "mov v1, vt4\n"+
           //project vertex
            "m44 op, va0, vc0");
 
            _fragmentShaderAssembler.assemble(Context3DProgramType.FRAGMENT,
            //environment
           "tex ft0,v0.xyz,fs0 <cube,clamp,linear>\n"+
            //refract
           "tex ft1,v1.xyz,fs0 <cube,clamp,linear>\n"+
            //mix them
          "sub ft3, ft0, ft1\n"+
         "mul ft3, ft3, fc0\n"+
         "add ft3, ft3, ft1\n"+
         //alpha
         "mov ft3.w, fc0.w\n"+
          //output color
          "mov oc, ft3"
           );
      }
       //переопределяем метод заливки ресурсов в видео-карту
      alternativa3d override function fillResources(resources : Dictionary, resourceType : Class) : void {
            //fill cubeTexture
          if (_cubeTexture != null &amp;&amp; A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_cubeTexture)) as Class, resourceType)) {
                resources[_cubeTexture] = true;
         }
           //upload program
            program.program = Main.instance.stage.stage3Ds[0].context3D.createProgram();
            program.program.upload(_vertexShaderAssembler.agalcode, _fragmentShaderAssembler.agalcode);
     }
 
        //draw each surface of object
       override alternativa3d function collectDraws(camera : Camera3D, surface : Surface, geometry : Geometry, lights : Vector.<Light3D>, lightsLength : int, objectRenderPriority : int = -1) : void {
            //draw each surface of object
       override alternativa3d function collectDraws(camera : Camera3D, surface : Surface, geometry : Geometry, lights : Vector., lightsLength : int, objectRenderPriority : int = -1) : void {
         //get link to object
            var object : Object3D = surface.object;
         //positionBuffer 
           var positionBuffer : VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
          //normalsBuffer 
            var normalsBuffer : VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
 
         var drawUnit : DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
            //setting buffers
           //point types float3,float3 and offsets 0,5
         drawUnit.setVertexBufferAt(0, positionBuffer, 0, "float3");
         drawUnit.setVertexBufferAt(1, normalsBuffer, 5, "float3");
          //project matrix
            drawUnit.setProjectionConstants(camera, 0, object.localToCameraTransform);
          //local-space camera position
           var cameraToLocalTransform : Transform3D = object.cameraToLocalTransform;
           drawUnit.setVertexConstantsFromNumbers(4, cameraToLocalTransform.d, cameraToLocalTransform.h, cameraToLocalTransform.l);
            //send to vertex shader 1, refractionIndex и refractionIndex^2 
            drawUnit.setVertexConstantsFromNumbers(5, 1, refractionIndex, refractionIndex*refractionIndex, 1);
          //calculating local to global transform
         var globalTransform : Transform3D = new Transform3D();
          globalTransform.copy(object.localToCameraTransform);
            globalTransform.append(camera.localToGlobalTransform);
          //send this transform
           drawUnit.setVertexConstantsFromTransform(6, globalTransform);
           //mix color
drawUnit.setFragmentConstantsFromNumbers(0,reflectionPower,reflectionPower,reflectionPower,alpha);
           //set cubemap texture   
            drawUnit.setTextureAt(0, _cubeTexture._texture);
            //add surface to draw
           camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : RenderPriority.TRANSPARENT_SORT);
          drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
           drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
        }
   }
}

SergeyGonchar
flash-developer
Visit Gonchar Website.

2 Responses to “(en) Environment and Refraction in Alternativa3D”

  1. […] Before start, I recommend you to read my previous article Environment and Refraction in Alternativa3D. […]

  2. Владимир says:

    import…….RenderPriority; -> import ……Renderer;

    RenderPriority.OPAQUE -> Renderer.OPAQUE

    заменить —- долго не понимал почему и как скомпилировать – надо было заменить (1) на (2)
    if u compile under 8.27+ version of alternative3D – u must change fisst to second – then u will normally compile it.

Leave a Reply

×
%d bloggers like this: