Почему GL разделяет `gl_Position` на W для вас, а не позволяет вам сделать это самостоятельно?

Примечание. Я понимаю основную математику. Я понимаю, что типичная функция perspective в различных математических библиотеках создает матрицу, которая преобразует значения z от -zNear в -zFar обратно в -1 к +1, но только если результат делится на w

Конкретный вопрос заключается в том, что GPU делает это для вас, а не для этого?

Другими словами, можно сказать, что графический процессор не волшебным образом разделил gl_Position на gl_Position.w и вместо этого вам пришлось делать это вручную, как в

 attribute vec4 position; uniform mat4 worldViewProjection; void main() { gl_Position = worldViewProjection * position; // imaginary version of GL where we must divide by W ourselves gl_Position /= gl_Position.w; } 

Что из-за этого ломается в этом воображаемом GL? Будет ли это работать или есть что-то в передаче значения, прежде чем он будет разделен на w который предоставляет дополнительную необходимую информацию для GPU?

Обратите внимание, что если я на самом деле это сделаю, перспектива отображения текстуры ломается.

 "use strict"; var m4 = twgl.m4; var gl = twgl.getWebGLContext(document.getElementById("c")); var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]); var bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 2); var tex = twgl.createTexture(gl, { min: gl.NEAREST, mag: gl.NEAREST, src: [ 255, 255, 255, 255, 192, 192, 192, 255, 192, 192, 192, 255, 255, 255, 255, 255, ], }); var uniforms = { u_diffuse: tex, }; function render(time) { time *= 0.001; twgl.resizeCanvasToDisplaySize(gl.canvas); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.enable(gl.DEPTH_TEST); gl.enable(gl.CULL_FACE); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); var projection = m4.perspective( 30 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 10); var eye = [1, 4, -6]; var target = [0, 0, 0]; var up = [0, 1, 0]; var camera = m4.lookAt(eye, target, up); var view = m4.inverse(camera); var viewProjection = m4.multiply(projection, view); var world = m4.rotationY(time); uniforms.u_worldInverseTranspose = m4.transpose(m4.inverse(world)); uniforms.u_worldViewProjection = m4.multiply(viewProjection, world); gl.useProgram(programInfo.program); twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); twgl.setUniforms(programInfo, uniforms); gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0); requestAnimationFrame(render); } requestAnimationFrame(render); 
 body { margin: 0; } canvas { display: block; width: 100vw; height: 100vh; } 
   uniform mat4 u_worldViewProjection; uniform mat4 u_worldInverseTranspose; attribute vec4 position; attribute vec3 normal; attribute vec2 texcoord; varying vec2 v_texcoord; varying vec3 v_normal; void main() { v_texcoord = texcoord; v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz; gl_Position = u_worldViewProjection * position; gl_Position /= gl_Position.w; }   precision mediump float; varying vec2 v_texcoord; varying vec3 v_normal; uniform sampler2D u_diffuse; void main() { vec4 diffuseColor = texture2D(u_diffuse, v_texcoord); vec3 a_normal = normalize(v_normal); float l = dot(a_normal, vec3(1, 0, 0)); gl_FragColor.rgb = diffuseColor.rgb * (l * 0.5 + 0.5); gl_FragColor.a = diffuseColor.a; }    

Но, так это потому, что на GPU на самом деле нужны z и w, чтобы быть разными или это просто графический процессор, а другой дизайн мог бы получить необходимую информацию, если бы мы разделили себя?

Обновить:

Задав этот вопрос, я закончил тем, что написал эту статью, которая иллюстрирует перспективную интерполяцию .

Причина в том, что не только gl_Position делится на однородную координату, но и все другие интерполированные изменения. Это называется перспективной правильной интерполяцией, которая требует деления после интерполяции (и, следовательно, после растеризации). Таким образом, разделение в вершинном шейдере просто не работает. См. Также этот пост .

Я хотел бы рассказать о ответе BDL. Речь идет не только о перспективной интерполяции. Это также касается отсечения . Пространство, значение которого должно быть указано в gl_Position называется пространством клипов , и это до деления на w.

Общий объем клипа OpenGL (по умолчанию) определяется для выполнения

 -1 <= x,y,z <= 1 (in NDC coordinates). 

Однако, когда вы смотрите на это ограничение перед делением, вы получите

 -w <= x,y,z <= w (in clip space, with w varying per vertex) 

однако, это только половина истины, так как и все точки пространства клипов, заполняющие этот, полностью заполнят ограничение NDC после деления

  w <= x,y,z <= -w (in clip space) 

Дело здесь в том, что точки за камерой будут преобразованы в где-то перед камерой , зеркалированные (поскольку x/-1 совпадает с -x/1 ). Это также происходит с координатой z . Можно утверждать, что это не имеет значения, потому что любая точка за камерой проецируется сзади (в смысле более далекой, чем) дальняя, согласно конструкции типичной матрицы проекции, поэтому она будет лежать вне объема просмотра в любом случае.

Но если у вас есть примитив, где по крайней мере одна точка находится внутри объема представления, и по крайней мере одна точка находится за камерой, вы должны иметь примитив, который также пересекает ближнюю плоскость. Однако, после деления на w , он будет теперь пересекать far плоскость! , Таким образом, отсечение в пространстве NDC, после разделения, намного сложнее получить право. Я попытался представить это на этом рисунке:

сверху вниз пространство зрения и NDC с и без обрезки (рисунок является масштабным, диапазон глубины проецирования намного короче, чем обычно использовали бы люди, чтобы лучше проиллюстрировать проблему).

Вырезание выполняется как этап фиксированной функции в оборудовании, и это необходимо сделать перед делением, поэтому вы должны обеспечить правильные координаты клипа для работы.

(Примечание: фактические графические процессоры могут вообще не использовать дополнительный этап отсечения, на самом деле они могут также использовать растеризатор без клитора, как это предполагается в статье блога Фабиана Гизена . Существуют некоторые алгоритмы, такие как Olano and Greer (1997) . все работает, делая растрирование непосредственно в однородных координатах, поэтому нам все еще нужно w ...)

Это еще проще; отсечение происходит после затенения вершин. Если вершинный шейдер был разрешен (или, более строго, назначен), чтобы сделать перспективное разделение, обрезание должно было бы произойти в однородных координатах, что было бы очень неудобно. Атрибуты вершин по-прежнему линейны по координатам клипа, что делает обрезку детской игры вместо того, чтобы закрепить в однородных координатах:

v ‘= 1,0f / (lerp (1,0 / v0, 1,0 / v1, t))

Посмотрите, как это будет тяжелым делением? В координатах клипов это просто:

v ‘= lerp (v0, v1, t)

Это даже лучше: пределы отсечения в координатах клипа:

-w

Это означает, что расстояния до плоскостей клипов (слева и справа) тривиальны для вычисления в координатах клипа:

x – w и w – x. Просто так проще и эффективнее клипать в координатах клипов, что он просто делает весь смысл в мире, настаивая на том, что вершинные шейдерные выходы находятся в координатах клипа. Затем позвольте аппаратным средствам отсекать и делить на w-координату, так как нет причин оставлять его пользователю. Это также проще, так как нам не нужен пост-клип-вершинный шейдер (который также включает отображение в окно просмотра, но это еще одна история). То, как они его разработали, на самом деле довольно приятно. 🙂

  • Преобразование из GLSurfaceView в TextureView (через GLTextureView)
  • Можно ли вращать объект вокруг своей оси, а не вокруг оси базовой оси?
  • Android OpenGL .OBJ загрузчик файлов
  • Расширения OpenGL, доступные на разных устройствах Android
  • Сжатие текстуры Android OpenGL
  • libgdx Сценарий SpriteBatch для текстуры
  • Преобразование YUV в RGB с помощью fragmentарного шейдера
  • Изменение выхода камеры с использованием SurfaceTexture и OpenGL
  • Рендеринг текстуры без использования двух текстур на iPhone
  • Учебники и библиотеки для игр OpenGL-ES на Android
  • Соответствующее умножение матриц для вращения / перевода
  • Interesting Posts

    Выполнение команды, хранящейся в переменной из Powershell

    Трюки для управления доступной памятью в сеансе R

    Ошибка Android в Eclipse: «Невозможно выполнить dex: невозможно слить новый индекс 65799 в инструкцию без jumbo!»

    обновить fragment UI от fragmentа.

    Показать путь к файлу EXE для запуска процессов в командной строке в Windows

    фильтрация NSArray в новый NSArray в объективе-c

    Безмолвно игнорируется remove ()

    Что вы не можете сделать на VM Dalvik (виртуальная машина Android), которую вы можете использовать в Sun VM?

    Быстрый потолок целочисленного деления в C / C ++

    Функция SUMIFS в Google Spreadsheet

    Несколько шлюзов по умолчанию

    Как установить динамический controller для директив?

    Разница между оправданием, выравниванием элементов и выравниванием содержимого в CSS Grid

    Как назвать / описать кнопку запуска Windows 7?

    Устаревшая анимация SMIL SVG заменена эффектами CSS или веб-анимации (наведите курсор, щелкните)

    Давайте будем гением компьютера.