@@ -8,13 +8,14 @@
package alternativa . engine3d . shadows {
import alternativa . engine3d . alternativa3d ;
import alternativa . engine3d . core . BoundBox ;
import alternativa . engine3d . core . Camera3D ;
import alternativa . engine3d . core . DrawUnit ;
import alternativa . engine3d . core . Object3D ;
import alternativa . engine3d . core . Renderer ;
import alternativa . engine3d . core . Transform3D ;
import alternativa . engine3d . core . VertexAttributes ;
import alternativa . engine3d . core . View ;
import alternativa . engine3d . lights . OmniLight ;
import alternativa . engine3d . materials . Material ;
import alternativa . engine3d . materials . ShaderProgram ;
import alternativa . engine3d . materials . TextureMaterial ;
@@ -41,18 +42,31 @@ package alternativa.engine3d.shadows {
public class OmniLightShadow extends Shadow {
// TODO: calculate bias automaticaly
/**
* Степень корректирующего смещения пространства карты теней для избавления от артефактов самозатенения .
* Degree of correcting offset of shadow map space. It need for getting rid of self-shadowing artifacts .
*/
public var biasMultiplier : Number = 0.99 ;
public var biasMultiplier : Number = 0.97 ;
private static const DIFFERENCE_MULTIPLIER : Number = 32768 ;
private static const DEBUG_TYPE : String = "Sphere" ; // Box
/**
* @private
*/
alternativa3d static var debugRadiusScale : Number = 0.5 ;
private var renderer : Renderer = new Renderer ();
private var boundSize : Number = 1 ;
// radius of the light source
private var radius : Number = 100 ;
// cube map size
private var _mapSize : Number ;
private var _pcfOffset : Number ;
private var cubeShadowMap : CubeTexture ;
// Sides cameras
private var cameras : Vector . < Camera3D > = new Vector . < Camera3D > ();
private var debugObject : Mesh ;
@@ -60,17 +74,18 @@ package alternativa.engine3d.shadows {
private var _casters : Vector . < Object3D > = new Vector . < Object3D > ();
private var cachedContext : Context3D ;
private var programs : Dictionary = new Dictionary ();
private var actualCasters : Vector . < Object3D > = new Vector . < Object3D > ();
private var actualCastersCount : int ;
private var edgeCameraToCasterTransform : Transform3D = new Transform3D ();
// caster -> cube face
private var casterToEdgedCameraTransform : Transform3D = new Transform3D ();
// object -> light
private var objectToLightTransform : Transform3D = new Transform3D ();
// casters count in edge
private var prevActualCastersMask : int ;
private var prevActualCaster Cou ntForEdge : Vector . < int > = new Vector . < int > ( 6 ) ;
private var cached Context : Context3D ;
private var programs : Dictionary = new Dictionary ();
/**
* Создает экземпляр OmniLightShadow.
@@ -78,25 +93,27 @@ package alternativa.engine3d.shadows {
* @param pcfOffset Смягчение границ тени.
*/
public function OmniLightShadow ( mapSize : int = 128 , pcfOffset : Number = 0 ) {
sections = new SectionPlane ( 0x11 , 0x22 , 0 xC ); // RU
sections . next = new SectionPlane ( 0x12 , 0x21 , 0 xC ); // LU
sections . next . next = new SectionPlane ( 0x14 , 0x28 , 0x3 ); // FU
sections . next . next . next = new SectionPlane ( 0x18 , 0x24 , 0x3 ); // BU
sections . next . next . next . next = new SectionPlane ( 0x5 , 0 xA , 0x30 ); // RF
sections . next . next . next . next . next = new SectionPlane ( 0x9 , 0x6 , 0x30 ); // RB
this . mapSize = mapSize ;
this . pcfOffset = pcfOffset ;
vertexShadowProcedure = getVShader ();
type = _pcfOffset > 0 ? "OS" : "os" ;
fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF () : getFShader ();
debugMaterial = new ShadowDebugMaterial ();
debugMaterial . alpha = 1.0 ;
debugMaterial . alpha = 0.3 ;
for ( var i : int = 0 ; i < 6 ; i ++ ) {
// создаем камеры
var cam : Camera3D = new Camera3D ( 1 , boundSize );
var cam : Camera3D = new Camera3D ( radius / 1000 , radius );
cam . fov = 1.910633237 ;
cam . view = new View ( boundSize , boundSize );
cam . renderer = renderer ;
cameras [ i ] = cam ;
prevActualCasterCountForEdge [ i ] = 0 ;
}
// Left
@@ -126,125 +143,91 @@ package alternativa.engine3d.shadows {
cameras [ 4 ]. composeTransforms ();
}
/**
* @private
*/
alternativa3d function setBoundSize ( value : Number ) : void {
this . boundSize = value ;
for ( var i : int = 0 ; i < 6 ; i ++ ) {
var cam : Camera3D = cameras [ i ];
cam . view . width = cam . view . height = int ( value );
cam . farClipping = value ;
cam . calculateProjection ( value , value );
}
}
private function createDebugCube ( material : Material , context : Context3D ) : Mesh {
var mesh : Mesh = new Mesh ();
// TODO: определиться куб или сфера
// var geometry:Geometry = new Geometry(8);
// mesh.geometry = geometry;
//
// var attributes:Array = new Array();
// attributes[0] = VertexAttributes.POSITION;
// attributes[1] = VertexAttributes.POSITION;
// attributes[2] = VertexAttributes.POSITION;
// geometry.addVertexStream(attributes);
//
// geometry.setAttributeValues(VertexAttributes.POSITION, Vector.<Number>([-0.5, -0.5, -0.5,
// 0.5, -0.5, -0.5,
// 0.5, 0.5, -0.5,
// -0.5, 0.5, -0.5,
// -0.5, -0.5, 0.5,
// 0.5, -0.5, 0.5,
// 0.5, 0.5, 0.5,
// -0.5, 0.5, 0.5]));
//
// geometry.indices = Vector.<uint>([ 0, 1, 2, 3, 0, 2, 2, 1, 0, 3, 2, 0,
// 2, 6, 1, 1, 6, 2, 1, 6, 5, 5, 6, 1,
// 6, 4, 5, 5, 4, 6, 6, 4, 7, 7, 4, 6,
// 0, 7, 4, 4, 7, 0, 0, 7, 3, 3, 7, 0,
// 3, 6, 2, 2, 6, 3, 3, 7, 6, 6, 7, 3,
// 0, 5, 1, 1, 5, 0, 0, 4, 5, 5, 4, 0]);
//
// mesh.addSurface(material, 0, 24);
var sphere : GeoSphere = new GeoSphere ( 1 , 4 , false );
var geometry : Geometry = sphere . geometry ;
private function createDebugObject ( material : Material , context : Context3D ) : Mesh {
var geometry : Geometry ;
var mesh : Mesh ;
if ( DEBUG_TYPE == "Box" ) {
mesh = new Mesh ();
geometry = new Geometry ( 8 ) ;
mesh . geometry = geometry ;
mesh . addSurface ( material , 0 , geometry . numTriangles );
geometry . upload ( context );
var attributes : Array = new Array ( );
attributes [ 0 ] = VertexAttributes . POSITION ;
attributes [ 1 ] = VertexAttributes . POSITION ;
attributes [ 2 ] = VertexAttributes . POSITION ;
geometry . addVertexStream ( attributes );
geometry . setAttributeValues ( VertexAttributes . POSITION , Vector . < Number > ([
- 1 , - 1 , - 1 ,
1 , - 1 , - 1 ,
1 , 1 , - 1 ,
- 1 , 1 , - 1 ,
- 1 , - 1 , 1 ,
1 , - 1 , 1 ,
1 , 1 , 1 ,
- 1 , 1 , 1 ]));
geometry . indices = Vector . < uint > ([
0 , 1 , 2 , 3 , 0 , 2 , 2 , 1 , 0 , 3 , 2 , 0 ,
2 , 6 , 1 , 1 , 6 , 2 , 1 , 6 , 5 , 5 , 6 , 1 ,
6 , 4 , 5 , 5 , 4 , 6 , 6 , 4 , 7 , 7 , 4 , 6 ,
0 , 7 , 4 , 4 , 7 , 0 , 0 , 7 , 3 , 3 , 7 , 0 ,
3 , 6 , 2 , 2 , 6 , 3 , 3 , 7 , 6 , 6 , 7 , 3 ,
0 , 5 , 1 , 1 , 5 , 0 , 0 , 4 , 5 , 5 , 4 , 0 ]);
mesh . addSurface ( material , 0 , 24 );
} else {
mesh = new GeoSphere ( 1 , 4 , true );
// Create two side
var triangles : Vector . < uint > = mesh . geometry . indices ;
var numTriangles : int = triangles . length ;
for ( var i : int = 0 ; i < numTriangles ; i += 3 ) {
var a : uint = triangles [ i ];
var b : uint = triangles [ int ( i + 1 )];
var c : uint = triangles [ int ( i + 2 )];
triangles . push ( c , b , a );
}
mesh . geometry . indices = triangles ;
mesh . getSurface ( 0 ). numTriangles = triangles . length / 3 ;
mesh . setMaterialToAllSurfaces ( material );
}
mesh . geometry . upload ( context );
return mesh ;
}
// Вычисление шедоумапы
// Draw in shadow map
override alternativa3d function process ( camera : Camera3D ) : void {
var i : int ;
var j : int ;
var caster : Object3D ;
var context : Context3D = camera . context3D ;
var castersCount : int = _casters . length ;
// Отсечение кастеров, тени которых не видны
// Обработка смены контекста
// Checking changed context
if ( context != cachedContext ) {
programs = new Dictionary ();
cubeShadowMap = null ;
cachedContext = context ;
for ( i = 0 ; i < cameras . length ; i ++ ) {
cameras [ i ]. context3D = cachedContext ;
}
}
// Culling invisible casters
if ( cubeShadowMap == null ) {
cubeShadowMap = context . createCubeTexture ( _mapSize , Context3DTextureFormat . BGRA , true );
debugMaterial . cubeMap = cubeShadowMap ;
prevActualCastersMask = 63 ;
}
// Calculate parameters
radius = OmniLight ( _light ). attenuationEnd ;
for ( i = 0 ; i < 6 ; i ++ ) {
context . setRenderToTexture ( cubeShadowMap , true , 0 , i ) ;
context . cl ear( 1 , 0 , 0 , 0.3 ) ;
}
var cam : Camera3D = cameras [ i ] ;
cam . n earClipping = radius / 1000 ;
cam . farClipping = radius ;
cam . calculateProjection ( 1 , 1 );
}
// предрасчитаем некоторые матрицы трансформации
for ( j = 0 ; j < castersCount ; j ++ ) {
caster = _casters [ j ];
if ( caster . transformChanged ) caster . composeTransforms ();
caster . lightToLocalTransform . combine ( caster . cameraToLocalTransform , _light . localToCameraTransform );
caster . localToLightTransform . combine ( _light . cameraToLocalTransform , caster . localToCameraTransform );
var skin : Skin = caster as Skin ;
if ( skin != null ) {
// Расчет матриц джоинтов
for ( var child : Object3D = skin . childrenList ; child != null ; child = child . next ) {
if ( child . transformChanged ) child . composeTransforms ();
// Записываем в localToGlobalTransform матрицу перевода в скин
child . localToGlobalTransform . copy ( child . transform );
if ( child is Joint ) {
Joint ( child ). calculateTransform ();
}
skin . calculateJointsTransforms ( child );
}
}
else {
if ( caster . childrenList )
calculateChildrenTransforms ( caster );
}
}
// Пробегаемся по 6-и камерам
for ( i = 0 ; i < 6 ; i ++ ) {
// камера соответствующая грани куба
var edgeCamera : Camera3D = cameras [ i ];
// проверяем, есть ли видимые кастеры попадающие на грань куба
var castersCount : int = _casters . length ;
actualCastersCount = 0 ;
for ( j = 0 ; j < castersCount ; j ++ ) {
caster = _casters [ j ];
for ( i = 0 ; i < castersCount ; i ++ ) {
caster = _casters [ i ];
var visible : Boolean = caster . visible ;
var parent : Object3D = caster . _parent ;
@@ -252,51 +235,104 @@ package alternativa.engine3d.shadows {
visible = parent . visible ;
parent = parent . _parent ;
}
if ( visible ) {
// Проверка куллинга
// формируем actualCasters
calculateVisibility ( caster , edgeCamera );
// calculate transform matrices
_light . lightToObjectTransform . combine ( caster . cameraToLocalTransform , _light . localToCameraTransform );
caster . localToLightTransform . combine ( _light . cameraToLocalTransform , caster . localToCameraTransform );
// collect actualCasters for light
if ( caster . boundBox == null || OmniLight ( _light ). checkBound ( caster )){
actualCasters [ actualCastersCount ] = caster ;
actualCastersCount ++;
// Pack camera culling
caster . culling <<= 16 ;
if ( caster . boundBox != null ) {
// 1 - calculate planes in object space
calculatePlanes ( caster . localToLightTransform );
// 2 - check object location cameras (sections)
caster . culling |= recognizeObjectCameras ( caster . boundBox );
}
}
// update Skin Joints matrices
var skin : Skin = caster as Skin ;
if ( skin != null ) {
// Calculate joints matrices
for ( var child : Object3D = skin . childrenList ; child != null ; child = child . next ) {
if ( child . transformChanged ) child . composeTransforms ();
// Write transformToSkin matrix to localToGlobalTransform property
child . localToGlobalTransform . copy ( child . transform );
if ( child is Joint ) {
Joint ( child ). calculateTransform ();
}
skin . calculateJointsTransforms ( child );
}
}
if ( caster . childrenList != null ) collectActualChildren ( caster );
}
}
// Iterate through six cameras
for ( i = 0 ; i < 6 ; i ++ ) {
// Cube side camera
var edgeCamera : Camera3D = cameras [ i ];
var edgeBit : int = ( 1 << i );
if ( actualCastersCount > 0 ) {
// Настройка параметров рендеринга:
renderer . camera = edgeC amera;
renderer . camera = c amera;
context . setRenderToTexture ( cubeShadowMap , true , 0 , i );
context . clear ( 1 , 0 , 0 , 0.0 );
// Пробегаемся по кастерам
for ( j = 0 ; j < actualCastersCount ; j ++ ) {
caster = actualCasters [ j ];
// Проверить находится ли кастер в зоне 4-х плоскостей
if (( caster . culling & edgeBit )) {
// собираем матрицу перевода из кастера в пространство edgeCamera
casterToEdgedCameraTransform . combine ( edgeCamera . inverseTransform , caster . localToLightTransform );
// Собираем драуколлы для кастера и е г о дочерних объектов
collectDraws ( context , caster , edgeCamera );
}
}
// Отрисовка дроуколов
// if (renderer.drawUnits.length == 0) context.clear(0, 0, 0, 0.0);
// Drawing
renderer . render ( context );
prevActualCastersMask |= edgeBit ;
}
else {
// Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов
if ( prevActualCasterCountForEdge [ i ] != 0 ){
if (( prevActualCastersMask & edgeBit )){
context . setRenderToTexture ( cubeShadowMap , false , 0 , i );
context . clear ( 1 , 0 , 0 , 0 );
prevActualCastersMask &= ~ edgeBit ;
}
}
prevActualCasterCountForEdge [ i ] = actualCastersCount ;
}
context . setRenderToBackBuffer ();
// Unpack camera culling value
for ( j = 0 ; j < actualCastersCount ; j ++ ) {
caster = actualCasters [ j ];
// If there was -1, after shift it will be -1 too
caster . culling >>= 16 ;
}
if ( debug ) {
if ( actualCastersCount > 0 ) {
// Создаем дебаговый объект, если он не создан
// Create debug object if needed
if ( debugObject == null ) {
debugObject = createDebugCube ( debugMaterial , camera . context3D );
debugObject . scaleX = debugObject . scaleY = debugObject . scaleZ = boundSize / 12 ;
debugObject . composeTransforms ();
debugObject = createDebugObject ( debugMaterial , camera . context3D );
}
debugObject . scaleX = debugObject . scaleY = debugObject . scaleZ = debugRadiusScale ;
debugObject . composeTransforms ();
// Формируем матрицу трансформации для debugObject
debugObject . localToCameraTransform . combine ( _light . localToCameraTransform , debugObject . transform );
@@ -305,28 +341,38 @@ package alternativa.engine3d.shadows {
var debugSurface : Surface = debugObject . _surfaces [ 0 ];
debugMaterial . collectDraws ( camera , debugSurface , debugObject . geometry , null , 0 , false , - 1 );
}
actualCasters . length = 0 ;
}
private function collectActualChildren ( root : Object3D ) : void {
for ( var child : Object3D = root . childrenList ; child != null ; child = child . next ) {
if ( child . visible ){
// calculate transform matrices
_light . lightToObjectTransform . combine ( child . cameraToLocalTransform , _light . localToCameraTransform );
child . localToLightTransform . combine ( _light . cameraToLocalTransform , child . localToCameraTransform );
// collect actualCasters for light
if ( child . boundBox == null || OmniLight ( _light ). checkBound ( child )){
actualCasters [ actualCastersCount ] = child ;
actualCastersCount ++;
// Pack camera culling
child . culling <<= 16 ;
if ( child . boundBox != null ) {
// 1 - calculate planes in object space
calculatePlanes ( child . localToLightTransform );
// 2 - check object location cameras (sections)
child . culling |= recognizeObjectCameras ( child . boundBox );
}
}
// предрасчитывает матрицы для всех детей
// localToLightTransform, lightToLocalTransform, transform, и calculateTransform для Joint
private function calculateChildrenTransforms ( root : Object3D ) : void {
var childrenList : Object3D = root . childrenList ;
for ( var child : Object3D = childrenList ; child != null ; child = child . next ) {
// расчет матриц трансформаций для объектов
if ( child . transformChanged ) child . composeTransforms ();
child . localToLightTransform . combine ( root . localToLightTransform , child . transform );
child . lightToLocalTransform . combine ( child . inverseTransform , root . lightToLocalTransform );
// расчет матриц трансформаций для скинов
// update Skin Joints matrices
var skin : Skin = child as Skin ;
if ( skin != null ) {
// Расчет матриц джоинтов
// Calculate joints matrices
for ( var skinChild : Object3D = skin . childrenList ; skinChild != null ; skinChild = skinChild . next ) {
if ( skinChild . transformChanged ) skinChild . composeTransforms ();
// Записываем в localToGlobalTransform матрицу перевода в скин
// Write transformToSkin matrix to localToGlobalTransform property
skinChild . localToGlobalTransform . copy ( skinChild . transform );
if ( skinChild is Joint ) {
Joint ( skinChild ). calculateTransform ();
@@ -334,56 +380,169 @@ package alternativa.engine3d.shadows {
skin . calculateJointsTransforms ( skinChild );
}
}
else {
if ( child . childrenList )
calculateChildrenTransforms ( child );
if ( child . childrenList != null ) collectActualChildren ( child );
}
}
}
// собирает список actualCasters для одной из 6-и камер
private function calculateVisibility ( root : Object3D , camera : Camera3D ) : void {
var casterCulling : int ;
private var sections : SectionPlane ;
if ( root . visible ) {
var skin : Skin = root as Skin ;
private function calculatePlanes ( transform : Transform3D ) : void {
// DUBFLR
var planeRU : SectionPlane = sections ;
var planeLU : SectionPlane = sections . next ;
var planeFU : SectionPlane = sections . next . next ;
var planeBU : SectionPlane = sections . next . next . next ;
var planeRF : SectionPlane = sections . next . next . next . next ;
var planeRB : SectionPlane = sections . next . next . next . next . next ;
// Вычисляем результат кулинга для объекта
if ( root . boundBox != null ) {
edgeCameraToCasterTransform . combine ( root . lightToLocalTransform , camera . transform ) ;
camera . calculateFrustu m ( edgeCameraToCasterT ransform) ;
casterCulling = root . boundBox . checkFrustumCulling ( camera . frustum , 63 );
// 1, 0, 1
planeRU . x = transform . a + transform . i ;
planeRU . y = transform . b + transform . j ;
planeRU . z = transfor m. c + t ransform. k ;
planeRU . offset = - ( transform . d + transform . l );
// -1, 0, 1
planeLU . x = transform . i - transform . a ;
planeLU . y = transform . j - transform . b ;
planeLU . z = transform . k - transform . c ;
planeLU . offset = transform . d - transform . l ;
// 0, 1, 1
planeFU . x = transform . e + transform . i ;
planeFU . y = transform . f + transform . j ;
planeFU . z = transform . g + transform . k ;
planeFU . offset = - ( transform . h + transform . l );
// 0, -1, 1
planeBU . x = transform . i - transform . e ;
planeBU . y = transform . j - transform . f ;
planeBU . z = transform . k - transform . g ;
planeBU . offset = transform . h - transform . l ;
// 1, 1, 0
planeRF . x = transform . a + transform . e ;
planeRF . y = transform . b + transform . f ;
planeRF . z = transform . c + transform . g ;
planeRF . offset = - ( transform . d + transform . h );
// 1, -1, 0
planeRB . x = transform . a - transform . e ;
planeRB . y = transform . b - transform . f ;
planeRB . z = transform . c - transform . g ;
planeRB . offset = transform . h - transform . d ;
// var ax:Number = transform.c - transform.a + transform.b; // E
// var ay:Number = transform.g - transform.e + transform.f;
// var az:Number = transform.k - transform.i + transform.j;
// var bx:Number = transform.c - transform.a - transform.b; // H
// var by:Number = transform.g - transform.e - transform.f;
// var bz:Number = transform.k - transform.i - transform.j;
// planeRU.x = bz * ay - by * az;
// planeRU.y = bx * az - bz * ax;
// planeRU.z = by * ax - bx * ay;
// planeRU.offset = transform.d*planeRU.x + transform.h*planeRU.y + transform.l*planeRU.z;
//
// ax = transform.c + transform.a - transform.b; // D
// ay = transform.g + transform.e - transform.f;
// az = transform.k + transform.i - transform.j;
// bx = transform.c + transform.a + transform.b; // A
// by = transform.g + transform.e + transform.f;
// bz = transform.k + transform.i + transform.j;
// planeLU.x = bz * ay - by * az;
// planeLU.y = bx * az - bz * ax;
// planeLU.z = by * ax - bx * ay;
// planeLU.offset = transform.d*planeLU.x + transform.h*planeLU.y + transform.l*planeLU.z;
//
// ax = transform.c - transform.a - transform.b; // H
// ay = transform.g - transform.e - transform.f;
// az = transform.k - transform.i - transform.j;
// bx = transform.c + transform.a - transform.b; // D
// by = transform.g + transform.e - transform.f;
// bz = transform.k + transform.i - transform.j;
// planeFU.x = bz * ay - by * az;
// planeFU.y = bx * az - bz * ax;
// planeFU.z = by * ax - bx * ay;
// planeFU.offset = transform.d*planeFU.x + transform.h*planeFU.y + transform.l*planeFU.z;
//
// ax = transform.c + transform.a + transform.b; // A
// ay = transform.g + transform.e + transform.f;
// az = transform.k + transform.i + transform.j;
// bx = transform.c - transform.a + transform.b; // E
// by = transform.g - transform.e + transform.f;
// bz = transform.k - transform.i + transform.j;
// planeBU.x = bz * ay - by * az;
// planeBU.y = bx * az - bz * ax;
// planeBU.z = by * ax - bx * ay;
// planeBU.offset = transform.d*planeBU.x + transform.h*planeBU.y + transform.l*planeBU.z;
//
// ax = transform.a - transform.b + transform.c; // D
// ay = transform.e - transform.f + transform.g;
// az = transform.i - transform.j + transform.k;
// bx = transform.a - transform.b - transform.c; // C
// by = transform.e - transform.f - transform.g;
// bz = transform.i - transform.j - transform.k;
// planeRF.x = bz * ay - by * az;
// planeRF.y = bx * az - bz * ax;
// planeRF.z = by * ax - bx * ay;
// planeRF.offset = transform.d*planeRF.x + transform.h*planeRF.y + transform.l*planeRF.z;
//
// ax = transform.a + transform.b - transform.c; // B
// ay = transform.e + transform.f - transform.g;
// az = transform.i + transform.j - transform.k;
// bx = transform.a + transform.b + transform.c; // A
// by = transform.e + transform.f + transform.g;
// bz = transform.i + transform.j + transform.k;
// planeRB.x = bz * ay - by * az;
// planeRB.y = bx * az - bz * ax;
// planeRB.z = by * ax - bx * ay;
// planeRB.offset = transform.d*planeRB.x + transform.h*planeRB.y + transform.l*planeRB.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 {
casterCulling = 63 ;
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 ;
}
// Если Кулинг кастера дает положительный результат, тогда
if ( casterCulling ){
if ( skin ){
actualCasters [ actualCastersCount ++ ] = root ;
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 {
var childrenList : Object3D = root . childrenList ;
// Если есть дочерние объекты,
if ( childrenList != null ){
// Проверяем их на кулинг
for ( var child : Object3D = childrenList ; child ! = null ; child = child . next ) {
calculateVisibility ( child , c amera) ;
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 {
i f ( 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 . backC ameras ;
}
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 ;
}
// Если дочерних объектов нет
else {
// добавляем кастер в список актуальных кастеров
actualCasters [ actualCastersCount ++ ] = root ;
culling &= result | plane . unusedBits ;
}
return culling ;
}
}
}
}
private function collectDraws ( context : Context3D , caster : Object3D , edgeCamera : Camera3D ) : void {
// если объект является мешем, собираем для него дроуколы
var mesh : Mesh = caster as Mesh ;
if ( mesh != null && mesh . geometry != null ) {
@@ -458,19 +617,12 @@ package alternativa.engine3d.shadows {
drawUnit . setProjectionConstants ( edgeCamera , program . vertexShader . getVariableIndex ( "cProjMatrix" ) , casterToEdgedCameraTransform );
drawUnit . setVertexConstantsFromTransform ( program . vertexShader . getVariableIndex ( "cCasterToOmni" ) , caster . localToLightTransform );
drawUnit . setVertex ConstantsFromNumbers ( program . vertex Shader. getVariableIndex ( "cScale " ) , 255 / boundSize , 0 , 0 , 1 );
drawUnit . setFragmentConstantsFromNumbers ( program . fragmentShader . getVariableIndex ( "cConstants" ) , 1 / 255 , 0 , 0 , 1 );
drawUnit . setFragment ConstantsFromNumbers ( program . fragment Shader. getVariableIndex ( "cConstants " ) , 1 / 255, 0, 255/ radius , 1 );
renderer . addDrawUnit ( drawUnit , Renderer . OPAQUE );
}
}
var child : Object3D ;
for ( child = caster . childrenList ; child != null ; child = child . next ) {
if ( ! ( child as Joint ) && child . visible ) collectDraws ( context , child , edgeCamera );
}
}
/**
* @private
@@ -525,15 +677,10 @@ package alternativa.engine3d.shadows {
}
var proc : Procedure = Procedure . compileFromArray ([
"#c1=cScale" ,
"#v0=vDistance" ,
"m34 t0.xyz, i0, c2" ,
"dp3 t0.x, t0.xyz, t0.xyz " ,
"sqt t0.x, t0.x" , // x: [0, boundSize]
"mul t0.x, t0.x, c1.x" , // x: [0, 255]
"mov t0.w, c1.w" ,
"mov v0, t0" ,
"mov v0, t0.xyzx " ,
"m44 o0, i0, c0"
]);
@@ -550,13 +697,18 @@ package alternativa.engine3d.shadows {
}
}
fLinker . addProcedure ( Procedure . compileFromArray ([
"#v0=vDistance" , // x: [0, 255]
"#c0=cConstants" , // 1/255, 0, 0 , 1
"frc t0.y, v0.x" ,
"sub t0.x , v0.x, t 0.y " ,
"#v0=vDistance" , // xyz
"#c0=cConstants" , // 1/255, 0, 255/radius , 1
// calculate distance
"dp3 t0.z , v0.xyz , v 0.xyz ",
"sqt t0.z, t0.z" , // x: [0, radius]
"mul t0.z, t0.z, c0.z" , // x: [0, 255]
// codeing
"frc t0.y, t0.z" ,
"sub t0.x, t0.z, t0.y" ,
"mul t0.x, t0.x, c0.x" ,
"mov t0.zw, c0.zw" ,
"mov t0.w, c0.w" ,
"mov o0, t0"
]));
program = new ShaderProgram ( vLinker , fLinker );
@@ -570,7 +722,7 @@ package alternativa.engine3d.shadows {
//------------- ShadowMap Shader ----------
//------------- ShadowMap Shader in material ----------
/**
* @private
@@ -584,16 +736,13 @@ package alternativa.engine3d.shadows {
drawUnit . setTextureAt ( fragmentLinker . getVariableIndex ( "sCubeMap" ) , cubeShadowMap );
// Устанавливаем коеффициенты
// TODO: сделать множитель более корректный. Возможно 65536 (разрешающая способность глубины буфера).
if ( _pcfOffset > 0 ) {
var offset : Number = _pcfOffset * 0.0175 ; //1 градус
var offset : Number = Math . tan ( _pcfOffset /180*Math.PI)/ 3 ;
drawUnit . setFragmentConstantsFromNumbers ( fragmentLinker . getVariableIndex ( "cPCFOffsets" ) , - 3 /2, 1/ 16 , 0 , 0 );
drawUnit . setFragmentConstantsFromNumbers ( fragmentLinker . getVariableIndex ( "cConstants" ) , - 1 , 1 , 0 , offset / boundSize );
drawUnit . setFragmentConstantsFromNumbers ( fragmentLinker . getVariableIndex ( "cDecode" ) , - 10000 , - 10000 /255, biasMultiplier*10000/ boundSize , 10 );
}
else {
drawUnit . setFragmentConstantsFromNumbers ( fragmentLinker . getVariableIndex ( "cConstants" ) , - 10000 , - 10000 /255, biasMultiplier*10000/ boundSize , 1 );
drawUnit . setFragmentConstantsFromNumbers ( fragmentLinker . getVariableIndex ( "cConstants" ) , - 1 , 1 , 0 , offset );
drawUnit . setFragmentConstantsFromNumbers ( fragmentLinker . getVariableIndex ( "cDecode" ) , - DIFFERENCE_MULTIPLIER , - DIFFERENCE_MULTIPLIER /255, biasMultiplier*DIFFERENCE_MULTIPLIER/ radius , 10 );
} else {
drawUnit . setFragmentConstantsFromNumbers ( fragmentLinker . getVariableIndex ( "cConstants" ) , - DIFFERENCE_MULTIPLIER , - DIFFERENCE_MULTIPLIER /255, biasMultiplier*DIFFERENCE_MULTIPLIER/ radius , 1.0 );
}
}
@@ -602,11 +751,8 @@ package alternativa.engine3d.shadows {
"#v0=vSample" ,
"m34 t0.xyz, i0, c0" ,
"dp3 t0.w, t0.xyz, t0.xyz" ,
"sqt t0.w, t0.w" , // w: [0, boundSize]
// "div t0.xyz, t0.xyz, t0.w", // norm
"mov v0, t0"
"mov v0, t0.xyz "
] , "OmniShadowMapVertex" );
shader . assignVariableName ( VariableType . CONSTANT , 0 , "cObjectToLightTransform" , 3 );
return shader ;
@@ -620,14 +766,20 @@ package alternativa.engine3d.shadows {
];
var line : int = 3 ;
// Расстояние
shaderArr [ line ++ ] = "mov t0.z, v0.w" ; // w: [0, boundSize]
shaderArr [ line ++ ] = "tex t0.xy, v0, s0 <cube, linear>" ;
shaderArr [ line ++ ] = "dp3 t0.z, v0.xyz, v0.xyz" ;
shaderArr [ line ++ ] = "sqt t0.z, t0.z" ; // w: [0, radius]
shaderArr [ line ++ ] = "tex t0.xy, v0, s0 <cube, nearest>" ;
shaderArr [ line ++ ] = "dp3 t0.x, t0.xyz, c0.xyz" ; // декодируем, находим разницу между расстояниями и умножаем е е на большое число
// рассчитываем значение тени
shaderArr [ line ++ ] = "sat t0, t0.x" ;
shaderArr [ line ++ ] = "sat t0.x , t0.x" ;
shaderArr [ line ++ ] = "sub o0, c0.w, t0.x" ;
// shaderArr[line++] = "sat t0.x, t0.x";
// shaderArr[line++] = "sub t0.x, c0.w, t0.x";
// shaderArr[line++] = "sat t0.x, t0.x";
// shaderArr[line++] = "mov o0, t0.x";
return Procedure . compileFromArray ( shaderArr , "OmniShadowMapFragment" );
}
@@ -645,41 +797,43 @@ package alternativa.engine3d.shadows {
// допустимо использование временных переменных t0 t1 t2 t3
// v0 - sample
// v0.w - length(sample) [0, boundSize]
// ищем 2-а перпендикулярных вектора
// calculate 2 ortogonal vectors
// (-y, x, 0)
shaderArr [ line ++ ] = "mov t1.xyzw, v0.yxzw" ;
shaderArr [ line ++ ] = "mul t1.xyzw, t1.xyzw, c1.xyzz" ;
shaderArr [ line ++ ] = "crs t0.xyz, v0.xyz, t1.xyz" ;
// нормируем их
// normalize vectors
shaderArr [ line ++ ] = "nrm t0.xyz, t0.xyz" ;
shaderArr [ line ++ ] = "nrm t1.xyz, t1.xyz" ;
// задаем оффсеты
shaderArr [ line ++ ] = "mul t0.w, c1.w, v0.w " ; // с 1.w = offset/boundSiz e
shaderArr [ line ++ ] = "dp3 t3.z, v0.xyz, v0.xyz" ;
shaderArr [ line ++ ] = "sqt t3.z, t3.z " ; // distanc e
// apply pcf offset
shaderArr [ line ++ ] = "mul t0.w, c1.w, t3.z" ; // с 1.w = offset/radius
shaderArr [ line ++ ] = "mul t0.xyz, t0.xyz, t0.w" ;
shaderArr [ line ++ ] = "mul t1.xyz, t1.xyz, t0.w" ;
// --------- {13 opcode}
// t0, t1 - перпендикуляры ↑→
// t2 - текущий вектор
// t0, t1 - ortogonals ↑→
// t2 - current vector
// в v0.w, t3.z расстояние до объекта
// t3.xy - результат из текстуры
// t3.w - сумма sat-ов
// t3.z distance to object
// t3.xy - result from shadow map
// t3.w - summ of sat
// первая точка
// first point
shaderArr [ line ++ ] = "add t2.xyz, t0.xyz, t1.xyz" ;
shaderArr [ line ++ ] = "mul t2.xyz, t2.xyz, c2.xxx" ;
shaderArr [ line ++ ] = "add t2.xyz, t2.xyz, v0.xyz" ;
// получаем длинну из шадоумапы [0, 1]
shaderArr[ line ++ ] = "mov t3.z, v 0.w";
// shaderArr[line++] = "mov t3.z, t 0.w";
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, li near>" ;
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, nearest >" ;
shaderArr [ line ++ ] = "dp3 o0." + componentByIndex [ 0 ] + ", t3.xyz, c0.xyz" ; // декодируем, вычитаем, умножаем на большое число
//-----
@@ -687,7 +841,7 @@ package alternativa.engine3d.shadows {
for ( j = 1 ; j < 4 ; j ++ ) {
shaderArr [ line ++ ] = "add t2.xyz, t2.xyz, t1.xyz" ;
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, li near>" ;
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, nearest >" ;
shaderArr [ line ++ ] = "dp3 o0." + componentByIndex [ j ] + ", t3.xyz, c0.xyz" ; // декодируем, вычитаем, умножаем на большое число
}
@@ -699,13 +853,13 @@ package alternativa.engine3d.shadows {
for ( i = 0 ; i < 3 ; i ++ ) {
shaderArr [ line ++ ] = "add t2.xyz, t2.xyz, t0.xyz" ;
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, li near>" ;
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, nearest >" ;
shaderArr [ line ++ ] = "dp3 o0." + componentByIndex [ 0 ] + ", t3.xyz, c0.xyz" ; // декодируем, вычитаем, умножаем на большое число
for ( j = 1 ; j < 4 ; j ++ ){
shaderArr [ line ++ ] = ( i % 2 == 1 ) ? ( "add t2.xyz, t2.xyz, t1.xyz" ) : ( "sub t2.xyz, t2.xyz, t1.xyz" );
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, li near>" ;
shaderArr [ line ++ ] = "tex t3.xy, t2.xyz, s0 <cube, nearest >" ;
shaderArr [ line ++ ] = "dp3 o0." + componentByIndex [ j ] + ", t3.xyz, c0.xyz" ; // декодируем, вычитаем, умножаем на большое число
}
shaderArr [ line ++ ] = "sat o0, o0" ;
@@ -715,13 +869,12 @@ package alternativa.engine3d.shadows {
shaderArr [ line ++ ] = "sub o0, c1.y, t3.w" ;
//--------- {73 opcode}
//--------- {73 opcodes }
return Procedure . compileFromArray ( shaderArr , "OmniShadowMapFragment" );
}
private static const componentByIndex : Array = [ "x" , "y" , "z" , "w" ];
/**
* Добавляет <code>object</code> в список объектов, отбрасывающих тень.
* @param object Добавляемый объект.
@@ -732,6 +885,12 @@ package alternativa.engine3d.shadows {
}
}
public function removeCaster ( object : Object3D ) : void {
var index : int = _casters . indexOf ( object );
if ( index < 0 ) throw new Error ( "Caster not found" );
_casters [ index ] = _casters . pop ();
}
/**
* Очищает список объектов, отбрасывающих тень.
*/
@@ -754,8 +913,8 @@ package alternativa.engine3d.shadows {
this . _mapSize = value ;
if ( value < 2 ) {
throw new ArgumentError ( "Map size cannot be less than 2." );
} else if ( value > 2048 ) {
throw new ArgumentError ( "Map size exceeds maximum value 2048 ." );
} else if ( value > 1024 ) {
throw new ArgumentError ( "Map size exceeds maximum value 1024 ." );
}
if (( Math . log ( value ) / Math . LN2 % 1 ) != 0 ) {
throw new ArgumentError ( "Map size must be power of two." );
@@ -769,6 +928,7 @@ package alternativa.engine3d.shadows {
/**
* Смещение Percentage Closer Filtering. Этот способ фильтрации используется для смягчения границ тени.
* 1 pcfOffset equivalent 1 degree for all blur
*/
public function get pcfOffset () : Number {
return _pcfOffset ;
@@ -868,6 +1028,10 @@ class ShadowDebugMaterial extends Material {
}
}
private function copyDrawUnit ( source : DrawUnit , dest : DrawUnit ) : void {
}
private function setupProgram ( object : Object3D ) : ShaderProgram {
var vertexLinker : Linker = new Linker ( Context3DProgramType . VERTEX );
var positionVar : String = "aPosition" ;
@@ -899,3 +1063,23 @@ 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 unusedBits : int = 63 ;
public function SectionPlane ( frontCameras : int , backCameras : int , unused : int ) {
this . frontCameras = frontCameras ;
this . backCameras = backCameras ;
this . unusedBits = unused ;
}
}