|
|
« Предыдущая тема | Следующая тема » |
Опции темы | Опции просмотра |
|
|
|||||
Регистрация: Dec 2011
Сообщений: 17
|
Необычные эффекты во время 3D трансформаций?
Здравствуйте! Мне необходимо вращать и поступательно перемешать 3D контент в пространстве. Исходные данные: есть мешЪ. Его размеры составляют по ширине 320, по высоте 240, а в глубину доходит до 4000. Все треугольники направлены в одну сторону (на экран). Выглядит меш как плоскость, только в отдельных местах, как бы обрывы из-за того, что в этих местах треугольники отдалены от остальных по оси Z (облако точек). Меш состоит из 320*240*2 = 153600 треугольников. Формат вектора вершин [x, y, z, r, g, b]. Класс, который я использую для перспективы com.adobe.utils.PerspectiveMatrix3D.
agal код как у всех для таких случаев: const VERTEX_SHADER:String = "m44 op, va0, vc0 \n" + "mov v0, va1"; const FRAGMENT_SHADER:String = "mov oc, v0"; 1. При вращении вокруг осей X и Y меш визуально обрезается (см. изображение). 2. При сдвиге по X и по Y происходит не поступательное перемещение, а какая-то деформация похожая на изменение угла обзора с одновременным поворотом. 3. Те треугольники, которые находятся на отдалении (z > 100) не видны вообще. То есть они конечно есть, но находятся на таком отдалении(до 4000), что превращаются в точку. Должен отметить, что перемещение вдоль Z происходит правильно. Привожу кусок кода (цикл рендера): public function render(e:Event):void { if(!contextChanged)return; context3D.clear(0,0,0); //Матрица для меша var matrix3D:Matrix3D = new Matrix3D(); //Поворачиваем меш вокруг осей x, y, z matrix3D.appendRotation(rY, Vector3D.Y_AXIS); matrix3D.appendRotation(rX, Vector3D.X_AXIS); matrix3D.appendRotation(rZ, Vector3D.Z_AXIS); //Перемещаем меш вдоль осей x, y, z matrix3D.appendTranslation(offsetX, offsetY, offsetZ); var aspect:Number = 3/3; //Так и не понял в чем суть этих zNear и zFar - визуально одна и та же хрень. var zNear:Number = -5000; var zFar:Number = 5000; var fov:Number = this.fov*Math.PI/180; perspective.identity(); perspective.perspectiveFieldOfViewLH(fov, aspect, zNear, zFar); //Добавляем перспективу matrix3D.append(perspective); //Чтобы не переполнился вершинный буфер разбросал точки по нескольким вершинным буферам. for(var i:uint=0;i<amountOfSprites;i++) { //Геометрия сontext3D.setVertexBufferAt(0,sprite3Ds[i].vertexBuffer3D,0,Context3DVertexBufferFormat.FLOAT_3); context3D.setVertexBufferAt(1,sprite3Ds[i].vertexBuffer3D,3,Context3DVertexBufferFormat.FLOAT_3); context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,matrix3D); //Отрисовка context3D.drawTriangles(sprite3Ds[i].indexBuffer3D,0,sprite3Ds[i].numTriangles); } //Окончательный вывод изображения. context3D.present(); contextChanged = false; log("render OK"); } |
|
|||||
Проблемы в zNear. Это должно быть положительное число больше 0 и меньше zFar.
По мелочи - не нужно создавать Matrix3D каждый раз в render(), не нужно инициализировать perspective каждый раз, zFar = 5000 - это очень много, будут артефакты на дальних расстояниях. Цитата:
__________________
http://volgogradetzzz.blogspot.ru/ |
|
|||||
Регистрация: Dec 2011
Сообщений: 17
|
Спасибо за советы, но изменение zNear до 0.1 и zFar до 2000 ни к каким улучшениям не привело. По-прежнему этот проклятый срез присутствует и все остальные проблемные эффекты остались. Какие есть еще предложения. Приведу весь код:
1. PointCloudRender.as package { import com.adobe.utils.AGALMiniAssembler; import com.adobe.utils.PerspectiveMatrix3D; import flash.display.BlendMode; import flash.display.Sprite; import flash.display.Stage3D; import flash.display.TriangleCulling; import flash.display3D.Context3D; import flash.display3D.Context3DCompareMode; import flash.display3D.Context3DProfile; import flash.display3D.Context3DProgramType; import flash.display3D.Context3DRenderMode; import flash.display3D.Context3DTriangleFace; import flash.display3D.Context3DVertexBufferFormat; import flash.display3D.Program3D; import flash.display3D.VertexBuffer3D; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.MouseEvent; import flash.filters.DropShadowFilter; import flash.geom.Matrix3D; import flash.geom.PerspectiveProjection; import flash.geom.Point; import flash.geom.Vector3D; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFieldType; import flash.text.TextFormat; import flash.utils.ByteArray; import flash.utils.getTimer; [SWF(width=800, height=600, frameRate=30)] public class PointCloudRender extends Sprite { public function PointCloudRender() { if(stage) loadData() else addEventListener(Event.ADDED_TO_STAGE,loadData); } private function loadData(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, loadData); var urlrequest:URLRequest=new URLRequest("data.pcd"); var urlloader:URLLoader=new URLLoader(); urlloader.addEventListener(IOErrorEvent.IO_ERROR,onIOError); urlloader.addEventListener(Event.COMPLETE,onComplete); urlloader.dataFormat= URLLoaderDataFormat.BINARY; urlloader.load(urlrequest); function onIOError(e:IOErrorEvent):void { urlloader.removeEventListener(IOErrorEvent.IO_ERROR,onIOError); urlloader.removeEventListener(Event.COMPLETE,onComplete); } function onComplete(e:Event):void { urlloader.removeEventListener(IOErrorEvent.IO_ERROR,onIOError); urlloader.removeEventListener(Event.COMPLETE,onComplete); pointCloud=urlloader.data; callContext3D(); } } private var context3D:Context3D; private function callContext3D(e:Event=null):void { stage.addEventListener(MouseEvent.MOUSE_WHEEL,onMWheel); stage.addEventListener(MouseEvent.MOUSE_DOWN,onMDown); stage.addEventListener(MouseEvent.MOUSE_UP,onMUp); removeEventListener(Event.ADDED_TO_STAGE,callContext3D); if(!stage) { log("callContext3D stage == null"); return; } if(!stage.stage3Ds) { log("callContext3D stage3Ds == null"); return; } if(stage.stage3Ds.length == 0) { log("callContext3D stage3Ds length == 0"); return; } var stage3D:Stage3D = stage.stage3Ds[0]; if(!stage3D) { log("callContext3D stage3D == null"); return; } stage3D.removeEventListener(Event.CONTEXT3D_CREATE,initContext3D); stage3D.addEventListener(Event.CONTEXT3D_CREATE,initContext3D); stage3D.requestContext3D(Context3DRenderMode.AUTO); log("callContext3D OK"); } private var vertexShader:AGALMiniAssembler = new AGALMiniAssembler(); private var fragmentShader:AGALMiniAssembler = new AGALMiniAssembler(); private function initContext3D(e:Event):void { context3D = (e.target as Stage3D).context3D; //Режим ошибок. context3D.enableErrorChecking = true; //Кюлинг context3D.setCulling(Context3DTriangleFace.NONE); //Не знаю для чего это, но взял от сюда - https://gist.github.com/noonat/847106 //context3D.setDepthTest(true,Context3DCompareMode.LESS_EQUAL); //Ширина и высота frameBuffer-а var backBufferWidth:uint = stage.stageHeight; var backBufferHeight:uint = stage.stageHeight; //AntiAliasing var antiAlias:uint = 0//0,2,4,8,16 log("backBuffer",backBufferWidth,backBufferHeight); //Конфигурируем размеры. context3D.configureBackBuffer(backBufferWidth,backBufferHeight,antiAlias,true,true); //Наши шейдеры. const VERTEX_SHADER:String = //"mov op, va0 \n" + "m44 op, va0, vc0 \n" + "mov v0, va1"; const FRAGMENT_SHADER:String = "mov oc, v0"; //Наши скомпилированные шейдеры. vertexShader.assemble(Context3DProgramType.VERTEX,VERTEX_SHADER); fragmentShader.assemble(Context3DProgramType.FRAGMENT,FRAGMENT_SHADER); //Программа var program:Program3D = context3D.createProgram(); program.upload(vertexShader.agalcode,fragmentShader.agalcode); context3D.setProgram(program); // Создаем спрайты // var sprite3D:Sprite3D = new Sprite3D(context3D,Vector.<Number>([ // //xyz rgb // -0.3, 0.3, 0.0, 1.0, 0.0, 0.0, // // 0.3, 0.3, 0.0, 0.0, 0.0, 0.0, // 0.0,-0.3, 0.0, 1.0, 1.0, 0.0 // ]),Vector.<uint>([0,1,2])); // sprite3Ds.push(sprite3D); sprite3Ds = new Vector.<Sprite3D>(); //Переводим данные кинекта в треугольники. initPointCloud(320,240,1,true); amountOfSprites = sprite3Ds.length; contextChanged = true; //Начинаем рендерить removeEventListener(Event.ENTER_FRAME,render); addEventListener(Event.ENTER_FRAME,render); log("initContext3D OK"); } private var perspective:PerspectiveMatrix3D; private var modelMatrix:Matrix3D; private var perspectiveMatrix:Matrix3D; private var modelPerspectiveMatrix:Matrix3D; private var amountOfSprites:uint=0; private var sprite3Ds:Vector.<Sprite3D> = new Vector.<Sprite3D>(); private var contextChanged:Boolean=false; private var matrix3D:Matrix3D; public function render(e:Event):void { if(!contextChanged)return; context3D.clear(0,0,0); if(!matrix3D) { matrix3D = new Matrix3D(); } else { matrix3D.identity(); } matrix3D.appendRotation(rY, Vector3D.Y_AXIS); matrix3D.appendRotation(rX, Vector3D.X_AXIS); matrix3D.appendRotation(rZ, Vector3D.Z_AXIS); matrix3D.appendScale(scale,scale,scale); matrix3D.appendTranslation(offsetX, offsetY, offsetZ); if(!perspective) { perspective = new PerspectiveMatrix3D(); var aspect:Number = 3/3; var zNear:Number = 0.1; var zFar:Number = 2000; var fov:Number = this.fov*Math.PI/180; perspective.perspectiveFieldOfViewLH(fov, aspect, zNear, zFar); } matrix3D.append(perspective); for(var i:uint=0;i<amountOfSprites;i++) { //Геометрия context3D.setVertexBufferAt(0,sprite3Ds[i].vertexBuffer3D,0,Context3DVertexBufferFormat.FLOAT_3); context3D.setVertexBufferAt(1,sprite3Ds[i].vertexBuffer3D,3,Context3DVertexBufferFormat.FLOAT_3); context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,matrix3D); //Отрисовка context3D.drawTriangles(sprite3Ds[i].indexBuffer3D,0,sprite3Ds[i].numTriangles); } //Окончательный вывод изображения. context3D.present(); contextChanged = false; log("render OK"); } private var rY:Number=0; private var rX:Number=0; private var rZ:Number=0; private var offsetX:Number=0; private var offsetY:Number=0; private var offsetZ:Number= 2; private var scale:Number= 1; private var fov:Number=45; public var press:Boolean=false; public var ctrl:Boolean=false; public var shift:Boolean=false; public var lastMouse:Point; public function onMDown(e:MouseEvent):void { lastMouse=new Point(stage.mouseX,stage.mouseY); updateInput(); removeEventListener(Event.ENTER_FRAME,updateInput); addEventListener(Event.ENTER_FRAME,updateInput); ctrl=e.ctrlKey; shift = e.shiftKey; } public function onMUp(e:MouseEvent):void { removeEventListener(Event.ENTER_FRAME,updateInput); ctrl = false; shift = false; } public function onMWheel(e:MouseEvent):void { contextChanged = true; if(e.ctrlKey) { fov -= e.delta; perspective = null; log2("fov =",fov.toFixed(2)); } else { rZ -= e.delta; log2("rotationZ =",rZ.toFixed(2)); } } private var sZ:Number = 1; private function updateInput(e:Event=null):void { var currentMouse:Point=new Point(stage.mouseX,stage.mouseY); if(ctrl && shift) { scale += (currentMouse.x-lastMouse.x)*0.01; log2("scale =",scale); } else if(ctrl) { offsetZ += (currentMouse.x-lastMouse.x); log2("offsetZ =",offsetZ); } else if(shift) { offsetX += (currentMouse.x-lastMouse.x)*0.01; offsetY -= (currentMouse.y-lastMouse.y)*0.01; log2("offsetX =",offsetX.toFixed(2),"offsetY =",offsetY.toFixed(2)); } else { rY-= (currentMouse.x-lastMouse.x); rX-= (currentMouse.y-lastMouse.y); log2("rotationY =",rY.toFixed(2),"rotationX =",rX.toFixed(2)); } lastMouse=currentMouse; contextChanged = true; } private var pointCloud:ByteArray; public function initPointCloud(width:int=320,height:int=240,density:int=1,includeRGB:Boolean=false):void { var vertices:Vector.<Number> = new Vector.<Number>(); var indexes:Vector.<uint> = initIndexes2(width); var i:uint=0; var j:uint=0; var offset:Number = 0.5; var centerX:Number = width*0.5; var centerY:Number = height*0.5; pointCloud.position=0; if(!includeRGB) { while(pointCloud.bytesAvailable>0) { var x:Number = Number(pointCloud.readUnsignedByte()); x += pointCloud.readUnsignedByte() << 8; var y:Number = Number(pointCloud.readUnsignedByte()); y += pointCloud.readUnsignedByte() << 8; var z:Number = Number(pointCloud.readUnsignedByte()); z += pointCloud.readUnsignedByte() << 8; vertices.push(x); vertices.push(y); vertices.push(z*0.01); i++; } } else { while(pointCloud.bytesAvailable>0) { //1 байт 0-255 x = Number(pointCloud.readUnsignedByte()); //2 байт 255 - 25535 x += pointCloud.readUnsignedByte() << 8; //3 байт y = Number(pointCloud.readUnsignedByte()); //4 байт y += pointCloud.readUnsignedByte() << 8; //5 байт z = Number(pointCloud.readUnsignedByte()); //6 байт z += pointCloud.readUnsignedByte() << 8; //7 байт - RED 1 var r:Number = Number(pointCloud.readUnsignedByte()); //8 байт - RED 2 pointCloud.readUnsignedByte(); //9 байт - GREEN 1 var g:Number = Number(pointCloud.readUnsignedByte()); //10 байт - GREEN 2 pointCloud.readUnsignedByte(); //11 байт - BLUE 1 var b:Number = Number(pointCloud.readUnsignedByte()); //12 байт - BLUE 2 pointCloud.readUnsignedByte(); x-=centerX; y-=centerY; //z*= 0; r/=255; g/=255; b/=255; //Первая вершина треугольника vertices.push(x-offset); vertices.push(y-offset); vertices.push(z); vertices.push(r); vertices.push(g); vertices.push(b); //Вторая вершина треугольника vertices.push(x+offset); vertices.push(y-offset); vertices.push(z); vertices.push(r); vertices.push(g); vertices.push(b); //Третья вершина треугольника vertices.push(x+offset); vertices.push(y+offset); vertices.push(z); vertices.push(r); vertices.push(g); vertices.push(b); //Четвертая вершина треугольника vertices.push(x-offset); vertices.push(y+offset); vertices.push(z); vertices.push(r); vertices.push(g); vertices.push(b); i++; if(i>=width) { var sprite3D:Sprite3D = new Sprite3D(context3D,vertices,indexes); sprite3Ds.push(sprite3D); vertices = new Vector.<Number>(); i = 0; j++; } } } log("initPointCloud OK"); function initIndexes2(triangles:int=320):Vector.<uint> { var vi:Vector.<uint> = new Vector.<uint>(); var indexAmount:uint = triangles*4; for(var i:uint=0;i<indexAmount;i+=4) { vi.push(i); vi.push(i+1); vi.push(i+2); vi.push(i); vi.push(i+2); vi.push(i+3); } vi.fixed=true; return vi; } } private var tf:TextField; public var log:Function=drawText; public function clearLog():void { if(tf)tf.text=""; } private var previewText:String; private var previewCount:int=1; private var previewLength:int=0; public function drawText(...args):void { if(!tf) { tf=new TextField(); tf.textColor=0x0000ff; tf.type=TextFieldType.INPUT; tf.addEventListener(Event.CHANGE,posTf); tf.multiline=true; tf.autoSize="right"; var tfr:TextFormat = new TextFormat(); tfr.align ="right"; tf.defaultTextFormat=tfr; tf.setTextFormat(tf.defaultTextFormat); tf.filters=[new DropShadowFilter(0,0,0xffffff,1,5,5,3)]; } if(stage)stage.addChild(tf); var text:String=""; for(var i:uint=0;i<args.length;i++)text+=(i==0?"":" ")+String(args[i]); if(previewText==text) { previewCount++; if(previewCount==2) { var extendedText:String="[ "+previewCount+" ] "+text; tf.text=tf.text.substring(0,tf.text.length-text.length)+extendedText; previewLength=extendedText.length; } else { extendedText="[ "+previewCount+" ] "+text; tf.text=tf.text.substring(0,tf.text.length-previewLength)+extendedText; previewLength=extendedText.length; } } else { previewCount=1; previewLength=0; tf.appendText((tf.text==""?"":"\n")+text); } previewText=text; posTf(); function posTf(e:Event=null):void { tf.x=stage?stage.stageWidth-tf.width-10:10; tf.y=stage?stage.stageHeight-tf.height:300-tf.height; } } public var log2:Function = drawText2; private var tf2:TextField; private var previewText2:String; private var previewCount2:int=1; private var previewLength2:int=0; public function drawText2(...args):void { if(!tf2) { tf2=new TextField(); tf2.textColor=0x00ffff; tf2.type=TextFieldType.INPUT; tf2.addEventListener(Event.CHANGE,posTf); tf2.multiline=true; tf2.autoSize="right"; var tfr:TextFormat = new TextFormat(); tfr.align ="right"; tf2.defaultTextFormat=tfr; tf2.setTextFormat(tf2.defaultTextFormat); // tf2.filters=[new DropShadowFilter(0,0,0xffffff,1,5,5,3)]; } tf2.text=""; if(stage)stage.addChild(tf2); var text:String=""; for(var i:uint=0;i<args.length;i++)text+=(i==0?"":" ")+String(args[i]); if(previewText==text) { previewCount++; if(previewCount==2) { var extendedText:String="[ "+previewCount2+" ] "+text; tf2.text=tf2.text.substring(0,tf2.text.length-text.length)+extendedText; previewLength2=extendedText.length; } else { extendedText="[ "+previewCount2+" ] "+text; tf2.text=tf2.text.substring(0,tf2.text.length-previewLength2)+extendedText; previewLength2=extendedText.length; } } else { previewCount2=1; previewLength2=0; tf2.appendText((tf2.text==""?"":"\n")+text); } previewText2=text; posTf(); function posTf(e:Event=null):void { tf2.x=10; tf2.y=stage?stage.stageHeight-tf2.height:300-tf2.height; } } } } package { import flash.display3D.Context3D; import flash.display3D.IndexBuffer3D; import flash.display3D.VertexBuffer3D; import flash.utils.ByteArray; public class Sprite3D { public function Sprite3D(context:Context3D,vertices:Vector.<Number>,indexes:Vector.<uint>,vertexStride:uint=6) { _indexBuffer3D = context.createIndexBuffer(indexes.length); _indexBuffer3D.uploadFromVector(indexes,0,indexes.length); _numVerticies = vertices.length/vertexStride; _numTriangles = _numVerticies/2; _vertexBuffer3D = context.createVertexBuffer(_numVerticies,vertexStride); _vertexBuffer3D.uploadFromVector(vertices,0,_numVerticies); } private var _numVerticies:uint=3; public function get numVerticies():uint { return _numVerticies; } private var _numTriangles:uint=1; public function get numTriangles():uint { return _numTriangles; } private var _vertexBuffer3D:VertexBuffer3D; public function get vertexBuffer3D():VertexBuffer3D { return _vertexBuffer3D; } private var _indexBuffer3D:IndexBuffer3D; public function get indexBuffer3D():IndexBuffer3D { return _indexBuffer3D; } } } |
|
|||||
По-моему нужно у матрицы перспективы секущую плоскость дальше вперед подвинуть, тогда и обрезаться не будет.
|
|
|||||
Сделай
и будет счастье
__________________
http://volgogradetzzz.blogspot.ru/ |
|
|||||
Регистрация: Dec 2011
Сообщений: 17
|
Огромное вам спасибище Волгоградец . Ваше решение убрало все необычные эффекты. Я так понял суть заключалась в том, что матрицу передаваемую в контекст, оказывается, надо было транспонировать. Вот уж никогда не догадался бы.
|
Часовой пояс GMT +4, время: 20:49. |
|
« Предыдущая тема | Следующая тема » |
|
|