OmniShadowLight - new culling

This commit is contained in:
Leonid Gaev
2012-05-03 18:05:45 +06:00
parent 24dc612ae5
commit 38b6af446b
6 changed files with 398 additions and 191 deletions

View File

@@ -65,25 +65,32 @@ package alternativa.engine3d.core {
var side:int = 1; var side:int = 1;
for (var plane:CullingPlane = frustum; plane != null; plane = plane.next) { for (var plane:CullingPlane = frustum; plane != null; plane = plane.next) {
if (culling & side) { if (culling & side) {
if (plane.x >= 0) if (plane.y >= 0) if (plane.z >= 0) { if (plane.x >= 0)
if (plane.y >= 0)
if (plane.z >= 0) {
if (maxX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1; if (maxX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side); if (minX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else { } else {
if (maxX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1; if (maxX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side); if (minX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.z >= 0) { }
else
if (plane.z >= 0) {
if (maxX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1; if (maxX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side); if (minX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else { } else {
if (maxX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1; if (maxX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side); if (minX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.y >= 0) if (plane.z >= 0) { }
else if (plane.y >= 0)
if (plane.z >= 0) {
if (minX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1; if (minX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side); if (maxX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else { } else {
if (minX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1; if (minX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side); if (maxX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.z >= 0) { }
else if (plane.z >= 0) {
if (minX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1; if (minX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side); if (maxX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else { } else {

View File

@@ -302,6 +302,8 @@ package alternativa.engine3d.core {
*/ */
alternativa3d var culling:int; alternativa3d var culling:int;
public var cameras:uint;
/** /**
* @private * @private
*/ */

View File

@@ -32,7 +32,7 @@ package alternativa.engine3d.lights {
/** /**
* Distance from at which falloff is complete. * Distance from at which falloff is complete.
*/ */
public var _attenuationEnd:Number; public var attenuationEnd:Number;
/** /**
* Creates a OmniLight object. * Creates a OmniLight object.
@@ -207,23 +207,7 @@ package alternativa.engine3d.lights {
*/ */
override public function set shadow(value:Shadow):void { override public function set shadow(value:Shadow):void {
_shadow = value; _shadow = value;
_shadow._light = this; if (_shadow!=null) _shadow._light = this;
var omniShadow:OmniLightShadow = value as OmniLightShadow;
if (omniShadow!=null) omniShadow.setBoundSize(this.attenuationEnd*1.5);
}
public function get attenuationEnd():Number{
return _attenuationEnd;
}
/**
* @private
*/
public function set attenuationEnd(value:Number):void{
// TODO: recalculate BoundBox
_attenuationEnd = value;
var omniShadow:OmniLightShadow = shadow as OmniLightShadow;
if (omniShadow!=null) omniShadow.setBoundSize(this._attenuationEnd*1.0);
} }
} }
} }

View File

@@ -58,7 +58,7 @@ public class ParserA3D extends Parser {
* @param input <code>ByteArray</code> consists of A3D data. * @param input <code>ByteArray</code> consists of A3D data.
*/ */
public function parse(input:ByteArray):void { public function parse(input:ByteArray):void {
try { // try {
input.position = 0; input.position = 0;
var version:int = input.readByte(); var version:int = input.readByte();
if (version == 0) { if (version == 0) {
@@ -69,10 +69,10 @@ public class ParserA3D extends Parser {
// Bit of packing. It always equal to 1, because version 2 and above is always packed. // Bit of packing. It always equal to 1, because version 2 and above is always packed.
parseVersionOver1(input); parseVersionOver1(input);
} }
} catch (e:Error) { // } catch (e:Error) {
e.message = "Parsing failed: " + e.message; // e.message = "Parsing failed: " + e.message;
throw e; // throw e;
} // }
} }

View File

@@ -348,6 +348,8 @@ package alternativa.engine3d.shadows {
shadowMap = camera.context3D.createTexture(_mapSize, _mapSize, Context3DTextureFormat.BGRA, true); shadowMap = camera.context3D.createTexture(_mapSize, _mapSize, Context3DTextureFormat.BGRA, true);
debugTexture._texture = shadowMap; debugTexture._texture = shadowMap;
} }
// TODO Don't clear if there was no casters
camera.context3D.setRenderToTexture(shadowMap, true); camera.context3D.setRenderToTexture(shadowMap, true);
camera.context3D.clear(1, 0, 0, 0.3); camera.context3D.clear(1, 0, 0, 0.3);

View File

@@ -8,12 +8,16 @@
package alternativa.engine3d.shadows { package alternativa.engine3d.shadows {
import alternativa.engine3d.alternativa3d; import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D; import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.CullingPlane;
import alternativa.engine3d.core.DrawUnit; import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Object3D; import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Renderer; import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.core.Transform3D; import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes; import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.materials.Material; import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.ShaderProgram; import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.TextureMaterial; import alternativa.engine3d.materials.TextureMaterial;
@@ -36,6 +40,8 @@ package alternativa.engine3d.shadows {
import flash.display3D.textures.CubeTexture; import flash.display3D.textures.CubeTexture;
import flash.utils.Dictionary; import flash.utils.Dictionary;
import spark.effects.easing.Elastic;
use namespace alternativa3d; use namespace alternativa3d;
public class OmniLightShadow extends Shadow{ public class OmniLightShadow extends Shadow{
@@ -52,6 +58,7 @@ package alternativa.engine3d.shadows {
// cube map size // cube map size
private var _mapSize:Number; private var _mapSize:Number;
private var _pcfOffset:Number; private var _pcfOffset:Number;
private var cubeShadowMap:CubeTexture; private var cubeShadowMap:CubeTexture;
@@ -64,6 +71,9 @@ package alternativa.engine3d.shadows {
private var _casters:Vector.<Object3D> = new Vector.<Object3D>(); private var _casters:Vector.<Object3D> = new Vector.<Object3D>();
private var castersInLight:Vector.<Object3D> = new Vector.<Object3D>();
private var castersInLightCount:int;
private var actualCasters:Vector.<Object3D> = new Vector.<Object3D>(); private var actualCasters:Vector.<Object3D> = new Vector.<Object3D>();
private var actualCastersCount:int; private var actualCastersCount:int;
@@ -74,7 +84,7 @@ package alternativa.engine3d.shadows {
// object -> light // object -> light
private var objectToLightTransform:Transform3D = new Transform3D(); private var objectToLightTransform:Transform3D = new Transform3D();
// casters count in edge // casters count in edge
private var prevActualCasterCountForEdge:Vector.<int> = new Vector.<int>(6); private var prevActualCastersMask:int;
private var cachedContext:Context3D; private var cachedContext:Context3D;
private var programs:Dictionary = new Dictionary(); private var programs:Dictionary = new Dictionary();
@@ -85,6 +95,13 @@ package alternativa.engine3d.shadows {
* @param pcfOffset Смягчение границ тени. * @param pcfOffset Смягчение границ тени.
*/ */
public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) { public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) {
sections = new SectionPlane();
sections.next = new SectionPlane();
sections.next.next = new SectionPlane();
sections.next.next.next = new SectionPlane();
sections.next.next.next.next = new SectionPlane();
sections.next.next.next.next.next = new SectionPlane();
this.mapSize = mapSize; this.mapSize = mapSize;
this.pcfOffset = pcfOffset; this.pcfOffset = pcfOffset;
@@ -93,7 +110,7 @@ package alternativa.engine3d.shadows {
fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader();
debugMaterial = new ShadowDebugMaterial(); debugMaterial = new ShadowDebugMaterial();
debugMaterial.alpha = 1.0; debugMaterial.alpha = 0.3;
for (var i:int = 0; i < 6; i++) { for (var i:int = 0; i < 6; i++) {
// Create cameras // Create cameras
@@ -101,8 +118,6 @@ package alternativa.engine3d.shadows {
var cam:Camera3D = new Camera3D(10, radius); var cam:Camera3D = new Camera3D(10, radius);
cam.fov = 1.910633237; cam.fov = 1.910633237;
cameras[i] = cam; cameras[i] = cam;
prevActualCasterCountForEdge[i] = 0;
} }
// Left // Left
@@ -131,27 +146,16 @@ package alternativa.engine3d.shadows {
cameras[4].scaleY = -1; cameras[4].scaleY = -1;
cameras[4].composeTransforms(); cameras[4].composeTransforms();
// DUBFLR
// TODO: boundBox of light?
// TODO: remove setBoundSize
}
/** // TODO: overwrite calculateFrustum function or setTransformConstants function
* @private // TODO: 2 step culling. 1-culling by radius for light. 2-culling for current camera by 4 planes
*/
alternativa3d function setBoundSize(value:Number):void{
this.radius = value;
for (var i:int = 0; i < 6; i++) {
var cam:Camera3D = cameras[i];
cam.farClipping = value;
cam.calculateProjection(value,value);
}
} }
private function createDebugObject(material:Material, context:Context3D):Mesh{ private function createDebugObject(material:Material, context:Context3D):Mesh{
var geometry:Geometry; var geometry:Geometry;
var mesh:Mesh; var mesh:Mesh;
// TODO: определиться куб или сфера
var isBox:Boolean = false; var isBox:Boolean = false;
if (isBox) { if (isBox) {
mesh = new Mesh(); mesh = new Mesh();
@@ -207,24 +211,52 @@ package alternativa.engine3d.shadows {
if (cubeShadowMap == null) { if (cubeShadowMap == null) {
cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true); cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true);
debugMaterial.cubeMap = cubeShadowMap; debugMaterial.cubeMap = cubeShadowMap;
// TODO: not clear here prevActualCastersMask = 63;
for (i = 0; i < 6; i++) {
context.setRenderToTexture(cubeShadowMap, true, 0, i);
context.clear(1, 0, 0, 0.3);
}
} }
// Calculate parameters
radius = OmniLight(_light).attenuationEnd;
for (i = 0; i < 6; i++) {
var cam:Camera3D = cameras[i];
cam.farClipping = radius;
cam.calculateProjection(radius, radius);
}
var castersCount:int = _casters.length; var castersCount:int = _casters.length;
// calculating some transformation matrices actualCastersCount = 0;
// TODO: skip invisible objects
for (i = 0; i < castersCount; i++) { for (i = 0; i < castersCount; i++) {
caster = _casters[i]; caster = _casters[i];
if (caster.transformChanged) caster.composeTransforms(); var visible:Boolean = caster.visible;
var parent:Object3D = caster._parent;
while (visible && parent != null) {
visible = parent.visible;
parent = parent._parent;
}
caster.lightToLocalTransform.combine(caster.cameraToLocalTransform, _light.localToCameraTransform); if (visible) {
// calculate transform matrices
_light.lightToObjectTransform.combine(caster.cameraToLocalTransform, _light.localToCameraTransform);
caster.localToLightTransform.combine(_light.cameraToLocalTransform, caster.localToCameraTransform); caster.localToLightTransform.combine(_light.cameraToLocalTransform, caster.localToCameraTransform);
// collect actualCasters for light
if (caster.boundBox == null || OmniLight(_light).checkBound(caster)){
actualCasters[actualCastersCount] = caster;
actualCastersCount++;
if (caster.boundBox != null) {
// 1 - calculate planes in object space
calculatePlanes(_light.lightToObjectTransform);
// 2 - check object location cameras (sections)
caster.cameras = recognizeObjectCameras(caster.boundBox);
}
} else {
caster.cameras = 63;
}
// update Skin Joints matrices
var skin:Skin = caster as Skin; var skin:Skin = caster as Skin;
if (skin != null) { if (skin != null) {
// Calculate joints matrices // Calculate joints matrices
@@ -239,45 +271,29 @@ package alternativa.engine3d.shadows {
} }
} }
if (caster.childrenList != null) calculateChildrenTransforms(caster); if (caster.childrenList != null) collectActualChildren(caster);
} }
}
// Iterate through six cameras // Iterate through six cameras
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
// Cube side camera // Cube side camera
var edgeCamera:Camera3D = cameras[i]; var edgeCamera:Camera3D = cameras[i];
// проверяем, есть ли видимые кастеры попадающие на грань куба // проверяем, есть ли видимые кастеры попадающие на грань куба
actualCastersCount = 0; // var flipX:Boolean = edgeCamera.scaleX < 0;
numCulled = 0; // var flipY:Boolean = edgeCamera.scaleY < 0;
// edgeCamera.scaleX = 1;
// edgeCamera.scaleY = 1;
// edgeCamera.composeTransforms();
var flipX:Boolean = edgeCamera.scaleX < 0; // if (flipX) edgeCamera.scaleX = -1;
var flipY:Boolean = edgeCamera.scaleY < 0; // if (flipY) edgeCamera.scaleY = -1;
edgeCamera.scaleX = 1; // edgeCamera.composeTransforms();
edgeCamera.scaleY = 1;
edgeCamera.composeTransforms();
for (j = 0; j < castersCount; j++) {
caster = _casters[j];
var visible:Boolean = caster.visible;
var parent:Object3D = caster._parent;
while (visible && parent != null) {
visible = parent.visible;
parent = parent._parent;
}
if (visible) {
// Проверка куллинга
// формируем actualCasters
calculateVisibility(caster, edgeCamera);
}
}
// trace("face:" + i + " culled:" + numCulled + " rest:" + actualCastersCount);
if (flipX) edgeCamera.scaleX = -1;
if (flipY) edgeCamera.scaleY = -1;
edgeCamera.composeTransforms();
var edgeBit:int = (1<<i);
if (actualCastersCount > 0) { if (actualCastersCount > 0) {
// Настройка параметров рендеринга: // Настройка параметров рендеринга:
renderer.camera = camera; renderer.camera = camera;
@@ -287,33 +303,59 @@ package alternativa.engine3d.shadows {
// Пробегаемся по кастерам // Пробегаемся по кастерам
for (j = 0; j < actualCastersCount; j++) { for (j = 0; j < actualCastersCount; j++) {
caster = actualCasters[j]; caster = actualCasters[j];
// Проверить находится ли кастер в зоне 4-х плоскостей
if (caster.cameras & edgeBit) {
// собираем матрицу перевода из кастера в пространство edgeCamera // собираем матрицу перевода из кастера в пространство edgeCamera
casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform); casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform);
// Собираем драуколлы для кастера и его дочерних объектов // Собираем драуколлы для кастера и его дочерних объектов
collectDraws(context, caster, edgeCamera); collectDraws(context, caster, edgeCamera);
} }
}
if (renderer.drawUnits.length == 0) context.clear(0, 0, 0, 0.0);
// Отрисовка дроуколов // Отрисовка дроуколов
renderer.render(context); renderer.render(context);
prevActualCastersMask |= edgeBit;
} }
else{ else{
// Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов // Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов
if (prevActualCasterCountForEdge[i]!=0){
if (prevActualCastersMask & edgeBit){
context.setRenderToTexture(cubeShadowMap, false, 0, i); context.setRenderToTexture(cubeShadowMap, false, 0, i);
context.clear(1, 0, 0, 0); context.clear(0, 0, 0, 0);
prevActualCastersMask &= ~edgeBit;
} }
} }
prevActualCasterCountForEdge[i] = actualCastersCount;
} }
context.setRenderToBackBuffer(); context.setRenderToBackBuffer();
// // Пробегаемся по кастерам
// for (j = 0; j < actualCastersCount; j++) {
// caster = actualCasters[j];
// caster.culling &= 0x8000003F;
//
// // Проверить находится ли кастер в зоне 4-х плоскостей
// if (caster.culling & (edgeBit << 8)) {
// // собираем матрицу перевода из кастера в пространство edgeCamera
// casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform);
// // Собираем драуколлы для кастера и его дочерних объектов
// collectDraws(context, caster, edgeCamera);
// }
// }
if (debug) { if (debug) {
if (actualCastersCount > 0) { if (actualCastersCount > 0 || true) {
// TODO: draw debug mesh always (DirectionalLightShadow)
// Создаем дебаговый объект, если он не создан // Создаем дебаговый объект, если он не создан
if (debugObject == null) { if (debugObject == null) {
debugObject = createDebugObject(debugMaterial, camera.context3D); debugObject = createDebugObject(debugMaterial, camera.context3D);
debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius/12; // TODO: select wright radius
// debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius/12;
debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius;
debugObject.composeTransforms(); debugObject.composeTransforms();
} }
@@ -327,19 +369,155 @@ package alternativa.engine3d.shadows {
} }
} }
// Precalculate children matrices private var sections:SectionPlane;
// localToLightTransform, lightToLocalTransform, transform, и calculateTransform для Joint
private function calculateChildrenTransforms(root:Object3D):void{ private function calculatePlanes(lightToObjectTransform:Transform3D):void {
var planeRU:SectionPlane = sections;
var planeLU:SectionPlane = sections.next;
sections.x = 0.707;
sections.z = 0.707;
sections.offset = sections.x*lightToObjectTransform.d + sections.z*lightToObjectTransform.l;
//var RIGHT_SIDE:int = 0;
sections.frontCameras = 0x11;
sections.backCameras = 0x22;
// sections.unused = 0x33;
sections.unused = 0xC;
planeLU.x = -0.707;
planeLU.z = 0.707;
planeLU.offset = planeLU.x*lightToObjectTransform.d + planeLU.z*lightToObjectTransform.l;
planeLU.frontCameras = 0x12;
planeLU.backCameras = 0x21;
planeLU.unused = 0xC;
// var nearPlane:CullingPlane = sections;
// var farPlane:CullingPlane = nearPlane.next;
// var leftPlane:CullingPlane = farPlane.next;
// var rightPlane:CullingPlane = leftPlane.next;
// var topPlane:CullingPlane = rightPlane.next;
// var bottomPlane:CullingPlane = topPlane.next;
//
// var fa:Number = transform.a * correctionX;
// var fe:Number = transform.e * correctionX;
// var fi:Number = transform.i * correctionX;
// var fb:Number = transform.b * correctionY;
// var ff:Number = transform.f * correctionY;
// var fj:Number = transform.j * correctionY;
//
// var ax:Number = -fa - fb + transform.c;
// var ay:Number = -fe - ff + transform.g;
// var az:Number = -fi - fj + transform.k;
// var bx:Number = fa - fb + transform.c;
// var by:Number = fe - ff + transform.g;
// var bz:Number = fi - fj + transform.k;
// topPlane.x = bz * ay - by * az;
// topPlane.y = bx * az - bz * ax;
// topPlane.z = by * ax - bx * ay;
// topPlane.offset = transform.d * topPlane.x + transform.h * topPlane.y + transform.l * topPlane.z;
// // Right plane.
// ax = bx;
// ay = by;
// az = bz;
// bx = fa + fb + transform.c;
// by = fe + ff + transform.g;
// bz = fi + fj + transform.k;
// rightPlane.x = bz * ay - by * az;
// rightPlane.y = bx * az - bz * ax;
// rightPlane.z = by * ax - bx * ay;
// rightPlane.offset = transform.d * rightPlane.x + transform.h * rightPlane.y + transform.l * rightPlane.z;
// // Bottom plane.
// ax = bx;
// ay = by;
// az = bz;
// bx = -fa + fb + transform.c;
// by = -fe + ff + transform.g;
// bz = -fi + fj + transform.k;
// bottomPlane.x = bz*ay - by*az;
// bottomPlane.y = bx*az - bz*ax;
// bottomPlane.z = by*ax - bx*ay;
// bottomPlane.offset = transform.d*bottomPlane.x + transform.h*bottomPlane.y + transform.l*bottomPlane.z;
// // Left plane.
// ax = bx;
// ay = by;
// az = bz;
// bx = -fa - fb + transform.c;
// by = -fe - ff + transform.g;
// bz = -fi - fj + transform.k;
// leftPlane.x = bz*ay - by*az;
// leftPlane.y = bx*az - bz*ax;
// leftPlane.z = by*ax - bx*ay;
// leftPlane.offset = transform.d*leftPlane.x + transform.h*leftPlane.y + transform.l*leftPlane.z;
}
private function recognizeObjectCameras(bb:BoundBox):int {
var culling:int = 63;
for (var plane:SectionPlane = sections; plane != null; plane = plane.next) {
var result:int = 0;
if (plane.x >= 0)
if (plane.y >= 0)
if (plane.z >= 0) {
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.minY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
else
if (plane.z >= 0) {
if (bb.maxX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
if (bb.maxX*plane.x + bb.minY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.minX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
else if (plane.y >= 0)
if (plane.z >= 0) {
if (bb.minX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.minY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
if (bb.minX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
else if (plane.z >= 0) {
if (bb.minX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras;
} else {
if (bb.minX*plane.x + bb.minY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras;
if (bb.maxX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras;
}
culling &= result | plane.unused;
}
return culling;
}
private function collectActualChildren(root:Object3D):void{
for (var child:Object3D = root.childrenList; child != null; child = child.next) { for (var child:Object3D = root.childrenList; child != null; child = child.next) {
if (child.visible){
// расчет матриц трансформаций для объектов // calculate transform matrices
// if (child.transformChanged) child.composeTransforms(); _light.lightToObjectTransform.combine(child.cameraToLocalTransform, _light.localToCameraTransform);
// child.localToLightTransform.combine(root.localToLightTransform, child.transform);
// child.lightToLocalTransform.combine(child.inverseTransform, root.lightToLocalTransform);
child.lightToLocalTransform.combine(child.cameraToLocalTransform, _light.localToCameraTransform);
child.localToLightTransform.combine(_light.cameraToLocalTransform, child.localToCameraTransform); child.localToLightTransform.combine(_light.cameraToLocalTransform, child.localToCameraTransform);
// collect actualCasters for light
if (child.boundBox == null || OmniLight(_light).checkBound(child)){
actualCasters[actualCastersCount] = child;
actualCastersCount++;
if (child.boundBox != null) {
// 1 - calculate planes in object space
calculatePlanes(_light.lightToObjectTransform);
// 2 - check object location cameras (sections)
child.cameras = recognizeObjectCameras(child.boundBox);
}
} else {
child.cameras = 63;
}
// update Skin Joints matrices
var skin:Skin = child as Skin; var skin:Skin = child as Skin;
if (skin != null) { if (skin != null) {
// Calculate joints matrices // Calculate joints matrices
@@ -354,41 +532,41 @@ package alternativa.engine3d.shadows {
} }
} }
if (child.childrenList != null) calculateChildrenTransforms(child); if (child.childrenList != null) collectActualChildren(child);
}
} }
} }
private static var numCulled:int;
// собирает список actualCasters для одной из 6-и камер // // собирает список actualCasters для одной из 6-и камер
private function calculateVisibility(root:Object3D, camera:Camera3D):void{ // private function calculateVisibility(root:Object3D, camera:Camera3D):void{
var casterCulling:int; // var casterCulling:int;
//
if (root.visible) { // if (root.visible) {
// Вычисляем результат кулинга для объекта // // Вычисляем результат кулинга для объекта
if (root.boundBox != null) { // if (root.boundBox != null) {
edgeCameraToCasterTransform.combine(root.lightToLocalTransform, camera.transform); // edgeCameraToCasterTransform.combine(root.lightToLocalTransform, camera.transform);
camera.calculateFrustum(edgeCameraToCasterTransform); // camera.calculateFrustum(edgeCameraToCasterTransform);
casterCulling = root.boundBox.checkFrustumCulling(camera.frustum, 63); // casterCulling = root.boundBox.checkFrustumCulling(camera.frustum, 63);
} else { // } else {
casterCulling = 63; // casterCulling = 63;
} // }
//
if (casterCulling <= 0) numCulled++; // if (casterCulling <= 0) numCulled++;
//
// добавляем кастер в список актуальных кастеров // // добавляем кастер в список актуальных кастеров
if (casterCulling >= 0) { // if (casterCulling >= 0) {
actualCasters[actualCastersCount] = root; // actualCasters[actualCastersCount] = root;
actualCastersCount++ // actualCastersCount++
} // }
//
// Если есть дочерние объекты, // // Если есть дочерние объекты,
// Проверяем их на кулинг // // Проверяем их на кулинг
for (var child:Object3D = root.childrenList; child != null; child = child.next) { // for (var child:Object3D = root.childrenList; child != null; child = child.next) {
calculateVisibility(child, camera); // calculateVisibility(child, camera);
} // }
} // }
} // }
private function collectDraws(context:Context3D, caster:Object3D, edgeCamera:Camera3D):void{ private function collectDraws(context:Context3D, caster:Object3D, edgeCamera:Camera3D):void{
// если объект является мешем, собираем для него дроуколы // если объект является мешем, собираем для него дроуколы
@@ -587,10 +765,10 @@ package alternativa.engine3d.shadows {
// Устанавливаем коеффициенты // Устанавливаем коеффициенты
// TODO: сделать множитель более корректный. Возможно 65536 (разрешающая способность глубины буфера). // TODO: сделать множитель более корректный. Возможно 65536 (разрешающая способность глубины буфера).
if (_pcfOffset > 0) { if (_pcfOffset > 0) {
var offset:Number = Math.tan(_pcfOffset/180*Math.PI)/3;
var offset:Number = _pcfOffset*0.0175; //TODO: make equivalent 1 offset ~ 1 degree
drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3/2, 1/16, 0, 0); drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3/2, 1/16, 0, 0);
drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset/radius); drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset);
drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -10000, -10000/255, biasMultiplier*10000/radius, 10); drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -10000, -10000/255, biasMultiplier*10000/radius, 10);
} }
else{ else{
@@ -781,6 +959,7 @@ package alternativa.engine3d.shadows {
/** /**
* Смещение Percentage Closer Filtering. Этот способ фильтрации используется для смягчения границ тени. * Смещение Percentage Closer Filtering. Этот способ фильтрации используется для смягчения границ тени.
* 1 pcfOffset equivalent 1 degree for all blur
*/ */
public function get pcfOffset():Number { public function get pcfOffset():Number {
return _pcfOffset; return _pcfOffset;
@@ -816,6 +995,7 @@ import alternativa.engine3d.resources.Geometry;
import flash.display3D.Context3D; import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor; import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType; import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.VertexBuffer3D; import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.CubeTexture; import flash.display3D.textures.CubeTexture;
import flash.utils.Dictionary; import flash.utils.Dictionary;
@@ -870,16 +1050,34 @@ class ShadowDebugMaterial extends Material {
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cDecode"), 1, 1/255, 0, alpha); drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cDecode"), 1, 1/255, 0, alpha);
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sCubeMap"), cubeMap); drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sCubeMap"), cubeMap);
// TODO: draw two-sided debug mesh
var drawUnit2:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
// Установка стримов
drawUnit2.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
// Установка констант
drawUnit2.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform);
drawUnit2.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cDecode"), 1, 1/255, 0, alpha);
drawUnit2.setTextureAt(program.fragmentShader.getVariableIndex("sCubeMap"), cubeMap);
drawUnit2.culling = Context3DTriangleFace.BACK;
// Отправка на отрисовку // Отправка на отрисовку
if (alpha < 1) { if (alpha < 1) {
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA; drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
drawUnit2.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit2.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
camera.renderer.addDrawUnit(drawUnit2, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT); camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
} else { } else {
camera.renderer.addDrawUnit(drawUnit2, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE); camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
} }
} }
private function copyDrawUnit(source:DrawUnit, dest:DrawUnit):void {
}
private function setupProgram(object:Object3D):ShaderProgram { private function setupProgram(object:Object3D):ShaderProgram {
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX); var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var positionVar:String = "aPosition"; var positionVar:String = "aPosition";
@@ -911,3 +1109,17 @@ class ShadowDebugMaterial extends Material {
} }
class SectionPlane {
public var x:Number = 0;
public var y:Number = 0;
public var z:Number = 0;
public var offset:Number = 0;
public var next:SectionPlane;
public var frontCameras:int;
public var backCameras:int;
public var unused:int = 63;
}