Използване на WebGL платно с разделителна способност на пиксели при запазване на перспективата

Виждал съм този тип въпроси тук преди, но никой не ми даде пълен отговор, така че ще публикувам кода си и ще видя дали някой може да ми даде някаква представа. По принцип този код работи, но имам магическо z отместване от (височина * 1,205) [независимо от ширината] и нямам представа защо.

Задаване на размера и създаване на вектор за подравняване:

gl.viewportWidth = gl.canvas.width;
gl.viewportHeight = gl.canvas.height;
gl.viewportHalfWidth = gl.viewportWidth / 2;
gl.viewportHalfHeight = gl.viewportHeight / 2;
gl.viewportAspect = gl.viewportWidth / gl.viewportHeight;
gl.viewportZoom = -gl.viewportHeight * 1.205;
if (gl.alignmentTest) {
    gl.alignmentVertices = (new Buffer(    // TL, TM, BM, BR, TR, BL, TL
       [    1.0,                        -1.0,                    0.0,
        gl.viewportHalfWidth - 0.5,     -1.0,                    0.0,
        gl.viewportHalfWidth - 0.5, -(gl.viewportHeight - 1.0),  0.0,
        gl.viewportWidth - 1.0,     -(gl.viewportHeight - 1.0),  0.0,
        gl.viewportWidth - 1.0,         -1.0,                    0.0,
            1.0,                    -(gl.viewportHeight - 1.0),  0.0,
            1.0,                        -1.0,                    0.0], 3)).post();
}

Чертане на вектора за подравняване:

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
mat4.perspective(45, gl.viewportAspect, 0.1, 1000.0, gl.perspective.current);
gl.perspective.post();    // ^ writes directly to current perspective buffer

gl.modelView.resetToIdentity();

gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

// Translate to pixel resolution
gl.modelView.push();
gl.modelView.translate([-gl.viewportHalfWidth, gl.viewportHalfHeight, gl.viewportZoom]);
gl.modelView.post();

// Alignment test?
if (gl.alignmentTest && gl.alignmentVertices) {
    gl.red.useAsAttribute(gl.shaderProg.vertexColour);
    gl.zero.vec2.useAsAttribute(gl.shaderProg.vertexTextureCoord);
    gl.alignmentVertices.drawAsPrimitives(gl.shaderProg.vertexPosition, gl.LINE_STRIP);
}

gl.disable(gl.BLEND);

Въпросните линии са:

gl.viewportZoom = -gl.viewportHeight * 1.205;

и:

gl.modelView.translate([-gl.viewportHalfWidth, gl.viewportHalfHeight, gl.viewportZoom]);

Този код работи за всеки размер.

Ето екранна снимка с размери 700x300: http://oi49.tinypic.com/2my9ir7.jpg

И още едно на 400x600: http://oi46.tinypic.com/i2irh3.jpg

Така че въпросът е: Защо съществува този магически коефициент на мащабиране от 1,205? Или какво може да правя погрешно, за да се наложи?


person jimmetry    schedule 11.12.2012    source източник
comment
Не съм сигурен, че разбирам въпроса ви. Искате да нарисувате четворка, като използвате 3D математика, така че да рисува 1x1 пиксел? Първият очевиден отговор е защо използвате 3d математика, за да нарисувате 2d? Просто рисувайте в 2D с 2D математика. (goo.gl/xQvx1) Но ако по някаква причина трябва да рисувате в 3D, вторият отговор е да използвате отрографска матрица.   -  person gman    schedule 11.12.2012
comment
Примитивът, който рисувам, е само пример, за да се уверя, че пространството на WebGL е подравнено спрямо границата на платното. Този конкретен интерфейс трябва да интегрира класически 2D обекти в 3D пространство и също така да позволява 3D обекти да бъдат манипулирани от 2D интерфейс, така че опитът да се направи разграничение между двете и да се третират отделно не е идеален. И не би трябвало да има значение. Това, което имам, работи добре и не е наистина хакерско. Просто искам да знам защо изисква магическо число, за да мога да го заменя с правилното изчисление.   -  person jimmetry    schedule 11.12.2012
comment
Няма WebGL място. WebGL е просто правоъгълник от пиксели и някои GLSL функции, които предоставяте. Какво ще правите с него зависи от вас. WebGL не дефинира интервал. Ти правиш. Бихте ли обмислили да промените заглавието на въпроса си, така че да има някакъв смисъл? Какво ще кажете за Как да подравня четворка на текстурирана единица в 3D пространство, така че да се показва в 1x1 пиксела в платно?   -  person gman    schedule 11.12.2012
comment
съжалявам Заглавието наистина беше подвеждащо. Не питам толкова как - доволен съм от метода, който имам сега - а по-скоро защо [в крайна сметка получавам магическо число]. Много съм доволен от отговора на Кевин по-долу. Благодаря все пак.   -  person jimmetry    schedule 12.12.2012


Отговори (1)


Не съм изчислил математиката, но предполагам, че този фактор произтича от FOV и близката равнина на вашата перспектива проекционна матрица. Повечето приложения, които се опитват да получат пиксели 1:1, не използват също перспективна проекция.

Ако имате причина да използвате перспектива, тогава предлагам да не използвате изчисления на перспектива, базирани на FOV, а вместо това да използвате базирани на пресечена точка (glFrustum-подобно) — много по-лесно е действително да извлечете ефектите от пресечена проекция и можете да разберете това корекционен коефициент от първи принципи, вместо да го коригирате на ръка.


Това по същество е тригонометричен проблем. Представете си пресечения изглед, или по-скоро пирамидата на изгледа (т.е. неотсечена от близките и далечните равнини); има върха си в „позиция на камерата“ и четири наклонени лица. FOV, както е условно дефиниран, е равен на ъгъла между горната и долната повърхност (ето защо, както споменахте, ширината няма влияние). За вашите цели обаче параметърът, който ви интересува, не е ъгълът, а наклонът (съотношението на Z към движението X-или-Y) — откъдето идва произволната ви фигура.

Ако погледнем документацията за доброто старо gluPerspective (който Ще предположа, че използва същата математика като вашата перспективна операция), казва се: f = cotangent(fovy/2). Това е точно изчислението, което ни интересува. (Ако се опитвате да възпроизведете това, имайте предвид, че повечето тригонометрични функции в математическите библиотеки приемат ъгли в радиани, а не в градуси.)

Коефициентът 2 се получава, защото FOV конвенционално се измерва като пълен зрителен ъгъл, но стойността, която представлява интерес, е половината от това - ъгълът между „направо напред“ и „точно на ръба на изгледа“.

Както може би знаете, стойността на функцията котангенс (или тангенс) може да се интерпретира като наклон. Този наклон f по-горе ви казва точно съотношението между движението Z и движението X-или-Y; например, ако мащабирате обект с някаква стойност d и също така го преместите по оста Z с f*d, тогава няма да изглежда, че променя размера си. Това може да е точно това, което вашият viewportZoom вече прави:

f / 2 = cot(45° / 2) / 2 ≈ 1.207106

което е доста близо до твоето 1.205. Ако това наистина е така, не съм разбрал откъде идва факторът 2.

Всичко, което трябва да направите, е да изчислите f веднъж (или евентуално неговото реципрочно tan(fovy/2) в зависимост от това кое е по-удобно в крайна сметка) и да го използвате както за изчисляване на разстоянията ви за "увеличение", така и на вашата проекционна матрица.


(Между другото, кодът ви съдържа много неща, които не са WebGL, напр. Buffer, post, drawAsPrimitives и т.н. Би било потенциално полезно, ако споменете каква библиотека използвате.)

person Kevin Reid    schedule 11.12.2012
comment
Благодаря. Първоначално си помислих, че е свързано с FOV, но ако случаят беше такъв, щях да очаквам ширината да окаже влияние. Знаете ли за някакви добри ресурси, обясняващи как работи FOV? Всичко, което някога съм виждал, е това е твоето зрително поле. Задайте го на 45. Смесвам 2D обекти, 3D обекти и второ наслагване върху платно (за редактиране на интерфейса), така че имам нужда както от пълна перспектива, така и от 1:1 пиксели. Не мога да кажа, че знам нещо за фрустумите, наистина, но мислех, че зависят от проекционната матрица. Може би обаче нещо не разбирам. - person jimmetry; 11.12.2012
comment
(Относно библиотеката: тя е моя собствена и е много непълна. Ще се радвам да я споделя, ако хората искат да я използват, но прецених, че е достатъчно интуитивна за обхвата на въпроса.) - person jimmetry; 11.12.2012
comment
@jimmetry Добавих малко обсъждане на FOV математика. - person Kevin Reid; 11.12.2012
comment
Благодаря. Ще го разгледам допълнително, но отговорът ви има много смисъл, особено като FOV е само вертикален, така че го приех. - person jimmetry; 12.12.2012