CRT Display Shader
/**
* Copyright devon_o ( http://wonderfl.net/user/devon_o )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/ix4l
*/
package
{
import com.adobe.utils.AGALMiniAssembler;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.display3D.Context3D;
import flash.display3D.Context3DProfile;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DRenderMode;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.textures.Texture;
import flash.display3D.VertexBuffer3D;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.net.URLRequest;
import flash.system.LoaderContext;
/**
* CRT display.
* largely ported from http://blog.mrlxhg.com/2014/10/old-crt-display-shader-fallout-pipoy-screen/
* @author Devon O.
*/
[SWF(width='465', height='465', backgroundColor='#000000', frameRate='60')]
public class Main extends Sprite
{
private static var VERTEX_SHADER:String =
<![CDATA[
m44 op, va0, vc0
mov v0, va1
]]>;
private static var FRAGMENT_SHADER:String =
<![CDATA[
mov ft0.xy, v0.xy
sub ft0.xy, v0.xy, fc0.zz
mov ft0.z, fc0.x
dp3 ft0.w, ft0.xyz, ft0.xyz
mul ft0.z, ft0.w, fc4.y
add ft0.w, fc0.w, ft0.z
mul ft0.w, ft0.w, ft0.z
mul ft0.xy, ft0.ww, ft0.xy
add ft0.xy, ft0.xy, v0.xy
tex ft2, ft0.xy, fs0<2d, clamp, nearest, mipnone>
sge ft3.x, ft0.x, fc0.x
sge ft3.y, ft0.y, fc0.x
slt ft3.z, ft0.x, fc0.w
slt ft3.w, ft0.y, fc0.w
mul ft3.x, ft3.x, ft3.y
mul ft3.x, ft3.x, ft3.z
mul ft3.x, ft3.x, ft3.w
max ft4.x, ft2.x, ft2.y
max ft4.x, ft4.x, ft2.z
min ft4.y, ft2.x, ft2.y
min ft4.y, ft4.y, ft2.z
div ft4.y, ft4.y, fc2.z
add ft4.x, ft4.x, ft4.y
mov ft4.xyzw, ft4.xxxx
mul ft4.xyzw, ft4.xyzw, ft3.xxxx
mov ft2.x, ft0.y
mul ft2.x, ft2.x, fc1.w
mul ft2.x, ft2.x, fc4.z
sin ft2.x, ft2.x
mul ft2.x, ft2.x, fc0.y
sat ft2.x, ft2.x
mul ft2.x, ft2.x, fc0.y
mul ft2.x, ft2.x, fc4.w
add ft2.x, ft2.x, fc0.w
mov ft2.y, fc0.w
mov ft2.z, fc5.x
mul ft2.z, ft2.z, fc0.z
add ft2.z, ft2.z, ft0.y
mul ft2.z, ft2.z, fc1.w
mul ft2.z, ft2.z, fc3.x
sin ft2.z, ft2.z
mul ft2.z, ft2.z, fc2.w
add ft2.y, ft2.y, ft2.z
add ft2.z, ft0.y, fc5.x
mul ft2.z, ft2.z, fc1.w
mul ft2.z, ft2.z, fc2.z
sin ft2.z, ft2.z
mul ft2.z, ft2.z, fc2.w
add ft2.y, ft2.y, ft2.z
mul ft2.y, ft2.y, fc4.x
mul ft0.xyz, ft4.xyz, ft3.xxx
mul ft0.xyz, ft0.xyz, fc3.yzw
mul ft0.xyz, ft0.xyz, ft2.xxx
mul ft0.xyz, ft0.xyz, ft2.yyy
mov ft0.w, fc0.w
mov oc, ft0
]]>;
private var context:Context3D;
private var isReady:Boolean;
private var program:Program3D;
private var renderMatrix:Matrix3D;
private var vertexBuffer:VertexBuffer3D;
private var indexBuffer:IndexBuffer3D;
private var textureData:BitmapData;
private var texture:Texture;
private var textureWidth:int;
private var textureHeight:int;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
this.isReady = false;
this.renderMatrix = new Matrix3D();
loadImage();
startRender();
}
private function loadImage():void
{
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad);
l.load(new URLRequest("http://assets.wonderfl.net/images/related_images/1/12/1247/124761a085cc7a1704c9fb601fe9d35e2e5a8b2e"), new LoaderContext(true));
}
private function onImageLoad(event:Event=null):void
{
event.currentTarget.removeEventListener(Event.COMPLETE, onImageLoad);
var l:Loader = (event.currentTarget as LoaderInfo).loader;
this.textureData = (l.content as Bitmap).bitmapData;
requestContext();
}
private function startRender():void
{
addEventListener(Event.ENTER_FRAME, onFrame);
}
private function requestContext():void
{
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onStage3dContext);
stage.stage3Ds[0].requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.BASELINE);
}
private function onStage3dContext(e:Event):void
{
this.context = stage.stage3Ds[0].context3D;
this.context.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2, false, false);
createVertexBuffer();
createIndexBuffer();
createTextures();
createPrograms();
this.isReady = true;
}
private function createVertexBuffer():void
{
var vertices:Vector.<Number> = new <Number>[
// x y u v
-1.0, 1.0, 0, 0,
1.0, 1.0, 1, 0,
-1.0, -1.0, 0, 1,
1.0, -1.0, 1, 1 ];
this.vertexBuffer = this.context.createVertexBuffer(4, 4);
this.vertexBuffer.uploadFromVector(vertices, 0, 4);
}
private function createIndexBuffer():void
{
this.indexBuffer = this.context.createIndexBuffer(6);
// 2 triangles (0, 1, 2) & (1, 3, 2)
// 0 - 1
// | / |
// 2 - 3
this.indexBuffer.uploadFromVector(new <uint>[0, 1, 2, 1, 3, 2], 0, 6);
}
private function createTextures():void
{
this.textureWidth = this.textureData.width;
this.textureHeight = this.textureData.height;
this.texture = this.context.createTexture(this.textureWidth, this.textureHeight, Context3DTextureFormat.BGRA, false);
this.texture.uploadFromBitmapData(this.textureData);
this.textureData.dispose();
}
private function createPrograms():void
{
var vertexShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER);
var fragmentShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
fragmentShaderAssembler.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER);
this.program = this.context.createProgram();
this.program.upload(vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
}
private var fc0:Vector.<Number> = new <Number>[0.0, .25, .50, 1.0];
private var fc1:Vector.<Number> = new <Number>[Math.sqrt(.50), 2.5, 1.55, Math.PI];
private var fc2:Vector.<Number> = new <Number>[2.2, 1.4, 2.0, .2];
private var fc3:Vector.<Number> = new <Number>[3.5, 1, 1, 1];
private var fc4:Vector.<Number> = new <Number>[1, 1, 1, 1];
private var fc5:Vector.<Number> = new <Number>[1, 1, 1, 1];
private var time:Number = 0.0;
private function onFrame(e:Event):void
{
if (!this.isReady)
return;
this.context.clear(0, 0, 0, 1);
this.renderMatrix.identity();
this.renderMatrix.appendScale(this.textureWidth / stage.stageWidth, this.textureHeight / stage.stageHeight, 1.0);
this.context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, this.renderMatrix, true);
this.context.setVertexBufferAt(0, this.vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
this.context.setVertexBufferAt(1, this.vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
// screen tint
fc3[1] = 0.7; // red
fc3[2] = 1.0; // green
fc3[3] = 0.7; // blue
fc4[0] = 1.1; // screen intensity/brightness
fc4[1] = 0.75; // bulging distortion (0 = none)
fc4[2] = 256; // scanline frequency
fc4[3] = 4.0; // scanline intensity
this.time += 10/512;
fc5[0] = time;
this.context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, this.fc0, 1);
this.context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, this.fc1, 1);
this.context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, this.fc2, 1);
this.context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 3, this.fc3, 1);
this.context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 4, this.fc4, 1);
this.context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 5, this.fc5, 1);
this.context.setTextureAt(0, this.texture);
this.context.setProgram(this.program);
this.context.drawTriangles(this.indexBuffer);
this.context.present();
}
}
}