stage3d study - shadow on plane
/**
* Copyright codeonwort ( http://wonderfl.net/user/codeonwort )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/4Uec
*/
// forked from codeonwort's stage3d study - the tetrahedron
package {
import flash.display.Sprite
import flash.events.Event
public class Main extends Sprite {
public function Main() {
stage.scaleMode = "noScale"
stage.align = "LT"
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, initStage3D)
stage.stage3Ds[0].requestContext3D()
}
private function initStage3D(e:Event):void {
new Study_ShadowOnPlane(this, e.target.context3D)
}
}
}
//package
//{
import com.adobe.utils.AGALMiniAssembler;
import com.adobe.utils.PerspectiveMatrix3D;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display3D.Context3D;
import flash.display3D.Context3DTriangleFace
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.events.Event
import flash.geom.Matrix3D;
/**
* ...
* @author codeonwort
*/
internal class Study_Base
{
protected var document:Sprite, stage:Stage, context:Context3D
//protected var antiAlias:int = 4, enableDepthAndStencil:Boolean = false
protected var perspectiveTransform:PerspectiveMatrix3D
//protected var vertBuf:VertexBuffer3D
//protected var idxBuf:IndexBuffer3D
protected var vsAsm:AGALMiniAssembler
protected var fsAsm:AGALMiniAssembler
protected var program:Program3D
public function Study_Base(document:Sprite, context:Context3D)
{
this.document = document
stage = document.stage
this.context = context
stage.addEventListener("resize", resize)
vsAsm = new AGALMiniAssembler
fsAsm = new AGALMiniAssembler
perspectiveTransform = new PerspectiveMatrix3D
perspectiveTransform.perspectiveFieldOfViewLH(45, stage.stageWidth / stage.stageHeight, 0.1, 100)
program = context.createProgram()
setupContext()
}
protected function resize(e:Event = null):void {
setupContext()
}
protected function setupContext():void {
context.configureBackBuffer(stage.stageWidth, stage.stageHeight, 4, false)
}
// 셰이더 코드에 \n + 붙이기 귀찮아서
protected function unify(...commands):String {
var str:String = ""
for each(var cmd:String in commands) {
str += cmd + "\n"
}
return str
}
}
//}
//package
//{
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
/**
* ...
* @author ...
*/
internal class Geometry
{
private static const xAxis:Vector3D = new Vector3D(1, 0, 0)
private static const yAxis:Vector3D = new Vector3D(0, 1, 0)
private static const zAxis:Vector3D = new Vector3D(0, 0, 1)
private var _transform:Matrix3D
public var vbuf:Vector.<Number> // data for vertex buffer
public var ibuf:Vector.<uint> // data for index buffer
public function Geometry()
{
_transform = new Matrix3D
vbuf = new Vector.<Number>
ibuf = new Vector.<uint>
}
// append
public function move(x:Number, y:Number, z:Number):void {
_transform.appendTranslation(x, y, z)
}
public function spin(degrees:Number, axis:Vector3D, pivot:Vector3D=null):void {
_transform.appendRotation(degrees, axis, pivot)
}
public function spinX(degrees:Number, pivot:Vector3D = null):void {
_transform.appendRotation(degrees, xAxis, pivot)
}
public function spinY(degrees:Number, pivot:Vector3D = null):void {
_transform.appendRotation(degrees, yAxis, pivot)
}
public function spinZ(degrees:Number, pivot:Vector3D = null):void {
_transform.appendRotation(degrees, zAxis, pivot)
}
public function zoom(x:Number, y:Number, z:Number):void {
_transform.appendScale(x, y, z)
}
// prepend
public function premove(x:Number, y:Number, z:Number):void {
_transform.prependTranslation(x, y, z)
}
public function prespin(degrees:Number, axis:Vector3D, pivot:Vector3D=null):void {
_transform.prependRotation(degrees, axis, pivot)
}
public function prespinX(degrees:Number, pivot:Vector3D = null):void {
_transform.prependRotation(degrees, xAxis, pivot)
}
public function prespinY(degrees:Number, pivot:Vector3D = null):void {
_transform.prependRotation(degrees, yAxis, pivot)
}
public function prespinZ(degrees:Number, pivot:Vector3D = null):void {
_transform.prependRotation(degrees, zAxis, pivot)
}
public function prezoom(x:Number, y:Number, z:Number):void {
_transform.prependScale(x, y, z)
}
public function get transform():Matrix3D { return _transform }
}
//}
//package
//{
/**
* ...
* @author ...
*/
internal class TetrahedronGeometry extends Geometry
{
public function TetrahedronGeometry(size:Number=1, winding:int = 1)
{
var angle0:Number = Math.PI / 2
var angle_delta:Number = 120 * Math.PI / 180
var len:Number = size / Math.sqrt(3)
var r:Number = 0.20412 * size
vbuf.push(len * Math.cos(angle0), -r, len * Math.sin(angle0),
len * Math.cos(angle0 + angle_delta), -r, len * Math.sin(angle0 + angle_delta),
len * Math.cos(angle0 - angle_delta), -r, len * Math.sin(angle0 - angle_delta),
0, 0.61237 * size, 0)
if (winding == -1) {
ibuf.push(0, 2, 1,
1, 2, 3,
2, 0, 3,
0, 1, 3)
}else if (winding == 1) {
ibuf.push(1, 2, 0,
3, 2, 1,
3, 0, 2,
3, 1, 0)
}else throw new ArgumentError('winding should be 1 or -1')
}
}
//}
//package
//{
import flash.display.Sprite;
import flash.display3D.Context3D;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.geom.Vector3D;
/**
* 평면에 그림자 렌더링하기
* @author codeonwort
*/
internal class Study_ShadowOnPlane extends Study_Base
{
private var tet:TetrahedronGeometry
private var colorbuf:VertexBuffer3D
private var colorbuf2:VertexBuffer3D
private var ibuf:IndexBuffer3D
private var shadowColor:Vector.<Number> = Vector.<Number>([0, 0, 0, 0.6])
private var shadowProgram:Program3D
private var shadowProj:Vector.<Number>
public function Study_ShadowOnPlane(document:Sprite, context:Context3D)
{
super(document, context)
tet = new TetrahedronGeometry(1, 1)
tet.move(0, 0.5, 2)
var vbuf:VertexBuffer3D = context.createVertexBuffer(4, 3)
vbuf.uploadFromVector(tet.vbuf, 0, 4)
context.setVertexBufferAt(0, vbuf, 0, 'float3')
colorbuf = context.createVertexBuffer(4, 3)
colorbuf.uploadFromVector(
Vector.<Number>([0, 1, 1, /* r g b */
1, 0, 0,
1, 0, 1,
1, 1, 0]), 0, 4)
ibuf = context.createIndexBuffer(tet.ibuf.length)
ibuf.uploadFromVector(tet.ibuf, 0, tet.ibuf.length)
/* 정점에 월드 - 원근 변환 적용
va0: vertex position
va1: vertex color
vc0~3: world transform matrix
vc4~7: perspective transform matrix
*/
var vshader:String = <agal><![CDATA[
m44 vt0, va0, vc0
m44 op, vt0, vc4
mov v0, va1
]]></agal>
vsAsm.assemble('vertex', vshader)
fsAsm.assemble('fragment', 'mov oc, v0')
program.upload(vsAsm.agalcode, fsAsm.agalcode)
context.setProgramConstantsFromMatrix('vertex', 4, perspectiveTransform, true)
/* 그림자 투영을 위한 정점 셰이더 (월드 -> 그림자 -> 원근 변환)
va0: vertex position
va1: vertex color
vc0~3: world transform matrix
vc4~7: perspective transform matrix
vc8~11: shadow projection matrix
*/
var vshader2:String = <agal><![CDATA[
m44 vt0, va0, vc0
m44 vt0, vt0, vc8
m44 op, vt0, vc4
mov v0, va1
]]></agal>
/* 그림자를 위한 픽셀 셰이더
fc0: 그림자 색
*/
var fshader2:String = <agal><![CDATA[
mov oc, fc0
]]></agal>
shadowProgram = context.createProgram()
shadowProgram.upload(vsAsm.assemble('vertex', vshader2), fsAsm.assemble('fragment', fshader2))
var plane_n:Vector3D = new Vector3D(0, 4, -1)
var plane_r:Vector3D = new Vector3D(0, 0, 4)
var light_d:Vector3D = new Vector3D(0, -1, 0)
plane_n.normalize()
light_d.normalize()
shadowProj = makeShadowProj(plane_n, plane_r, light_d)
render()
document.addEventListener('enterFrame', render)
}
// 특정 평면에 대한 그림자 투영 행렬을 만든다.
// n: 평면의 단위 법선 벡터
// r: 평면이 포함하는 임의의 점
// L: 빛의 단위 방향 벡터
private function makeShadowProj(n:Vector3D, r:Vector3D, L:Vector3D):Vector.<Number> {
var nL:Number = n.dotProduct(L)
var nr:Number = n.dotProduct(r)
var proj:Vector.<Number> = new Vector.<Number>
proj.push(nL - n.x * L.x, -n.y * L.x, -n.z * L.x, nr * L.x)
proj.push(-n.x * L.y, nL - n.y * L.y, -n.z * L.y, nr * L.y)
proj.push(-n.x * L.z, -n.y * L.z, nL - n.z * L.z, nr * L.z)
proj.push(0, 0, 0, nL)
for (var i:int = 0 ; i < proj.length ; i++) {
proj[i] /= nL
}
/*
proj.push(nL - n.x * L.x, -n.y * L.x, -n.z * L.x, nr * L.x)
proj.push(-n.x * L.y, nL - n.y * L.y, -n.z * L.y, nr * L.y)
proj.push(-n.x * L.z, -n.y * L.z, nL - n.z * L.z, nr * L.z)
proj.push(0, 0, 0, nL)
*/
return proj
}
protected override function setupContext():void {
context.configureBackBuffer(stage.stageWidth, stage.stageHeight, 4, true)
context.setDepthTest(true, 'less')
}
private function render($:Object=null):void {
context.clear(.5, .5, .5, 1,
1 /*depth*/,
0 /*stencil*/)
tet.prespinX(3)
tet.prespinY(1)
context.setCulling('back')
context.setBlendFactors('one', 'zero')
context.setVertexBufferAt(1, colorbuf, 0, 'float3')
context.setProgram(program)
context.setProgramConstantsFromMatrix('vertex', 0, tet.transform, true)
context.drawTriangles(ibuf, 0, 4)
context.setBlendFactors('one', 'oneMinusSourceAlpha')
context.setProgram(shadowProgram)
context.setProgramConstantsFromVector('fragment', 0, shadowColor)
context.setProgramConstantsFromVector('vertex', 8, shadowProj, 4)
context.drawTriangles(ibuf, 0, 4)
context.present()
}
}
//}