цилиндр-самозванец в GLSL - PullRequest
       105

цилиндр-самозванец в GLSL

6 голосов
/ 07 марта 2012

Я разрабатываю небольшой инструмент для трехмерной визуализации молекул.Для своего проекта я решил сделать то, что сделал мистер "Брэд Ларсон" со своим программным обеспечением Apple " Molecules ".Ссылка, где вы можете найти небольшую презентацию используемой техники: Презентация программного обеспечения Брэда Ларссона

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

На данный момент мне удалось выполнить «Сферный самозванец» с помощью другого урока Ложь и самозванцы

, чтобы подвести итог вычислений сферного самозванца: сначаламы отправляем "положение сферы" и "радиус сферы" в "вершинный шейдер", который создаст в пространстве камеры квадрат, который всегда обращен к камере, после чего мы отправляем наш квадрат фрагментному шейдеру, где мы используем простойтрассировка лучей, чтобы найти, какой фрагмент квадрата включен в сферу, и, наконец, мы вычисляем нормаль и положение фрагмента для вычисления освещения.(Другое дело, что мы также пишем gl_fragdepth для придания глубины нашей сфере самозванца!)

Но теперь я заблокирован в вычислении цилиндрового самозванца, я пытаюсь провести параллель между сферическим самозванцем иЦилиндрический самозванец, но я ничего не нахожу, моя проблема в том, что для сферы это было немного легко, потому что сфера всегда одна и та же, независимо от того, как мы ее видим, мы всегда будем видеть одно и то же: «круг» и другоев том, что сфера была совершенно определена Матем, тогда мы можем легко найти положение и нормаль для вычисления освещения и создать нашего самозванца.

Для цилиндра это не одно и то же, и мне не удалось найти подсказку длямоделирование формы, которую можно использовать как «цилиндр-самозванец», потому что цилиндр показывает много разных форм в зависимости от угла, который мы видим!

поэтому я прошу вас спросить о решении или указании на мою проблему "цилиндрового самозванца".

Ответы [ 4 ]

3 голосов
/ 08 мая 2013

Я знаю, что этому вопросу больше года, но я все же хотел бы дать свои 2 цента.

Мне удалось изготовить цилиндровые самозванцы с другой техникой, я черпал вдохновение из кода Пимола. Вот основная стратегия:

1) Вы хотите нарисовать ограничивающую рамку ( кубоид ) для цилиндра. Для этого вам нужно 6 граней, что означает 18 треугольников и 36 вершин треугольников. Предполагая, что у вас нет доступа к геометрическим шейдерам, вы передаете вершинному шейдеру 36 раз начальную точку цилиндра, 36 раз направление цилиндра, и для каждой из этих вершин вы проходите соответствующую точку ограничительной рамки. , Например, вершина, связанная с точкой (0, 0, 0), означает, что она будет преобразована в левом нижнем углу ограничительной рамки, (1,1,1) означает противоположную по диагонали точку и т. Д.

2) В вершинном шейдере вы можете построить точки цилиндра, сместив каждую вершину (вы прошли 36 равных вершин) в соответствии с соответствующими точками, в которых вы прошли. В конце этого шага у вас должна быть ограничительная рамка для цилиндра.

3) Здесь вы должны восстановить точки на видимой поверхности ограничительной рамки. Начиная с полученной точки, вы должны выполнить пересечение луча с цилиндром.

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

Кстати, это очень сложная задача, если кому-то интересно, вот исходный код:

https://github.com/chemlab/chemlab/blob/master/chemlab/graphics/renderers/shaders/cylinderimp.frag

https://github.com/chemlab/chemlab/blob/master/chemlab/graphics/renderers/shaders/cylinderimp.vert

2 голосов
/ 28 мая 2013

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

Конечно, это способ трассировки лучей.Обратите внимание, что цилиндр, обращенный вверх в пространстве камеры, довольно легко реализовать.Например, пересечение со стороной может быть спроецировано на плоскость xz, это двумерная проблема линии, пересекающейся с окружностью.Получить верх и низ тоже не сложно, дана координата z пересечения, так что вы на самом деле знаете точку пересечения луча и плоскости круга, все, что вам нужно сделать, это проверить, находится ли он внутри круга.И в основном, вот и все, вы получаете две точки и возвращаете более близкую (нормали тоже довольно тривиальны).

И когда дело доходит до произвольной оси, это оказывается почти той же проблемой.Когда вы решаете уравнения в цилиндре с фиксированной осью, вы решаете их для параметра, который описывает, как долго вам нужно пройти от заданной точки в данном направлении, чтобы достичь цилиндра.Из «определения» этого вы должны заметить, что этот параметр не меняется, если вы вращаете мир.Таким образом, вы можете повернуть произвольную ось, чтобы стать осью y, решить проблему в пространстве, где уравнения проще, получить параметр для уравнения линии в этом пространстве, но вернуть результат в пространстве камеры.

ВыВы можете скачать файлы шейдеров с здесь .Просто изображение этого в действии: снимок экрана http://oi40.tinypic.com/2h5tqhy.jpg

Код, где происходит волшебство (это только долго, потому что оно полно комментариев, но сам код содержит максимум 50 строк):

void CylinderImpostor(out vec3 cameraPos, out vec3 cameraNormal)
{
    // First get the camera space direction of the ray.
    vec3 cameraPlanePos = vec3(mapping * max(cylRadius, cylHeight), 0.0) + cameraCylCenter;
    vec3 cameraRayDirection = normalize(cameraPlanePos);

    // Now transform data into Cylinder space wherethe cyl's symetry axis is up.
    vec3 cylCenter = cameraToCylinder * cameraCylCenter;
    vec3 rayDirection = normalize(cameraToCylinder * cameraPlanePos);


    // We will have to return the one from the intersection of the ray and circles,
    // and the ray and the side, that is closer to the camera. For that, we need to
    // store the results of the computations.
    vec3 circlePos, sidePos;
    vec3 circleNormal, sideNormal;
    bool circleIntersection = false, sideIntersection = false;

    // First check if the ray intersects with the top or bottom circle
    // Note that if the ray is parallel with the circles then we
    // definitely won't get any intersection (but we would divide with 0).
    if(rayDirection.y != 0.0){
        // What we know here is that the distance of the point's y coord
        // and the cylCenter is cylHeight, and the distance from the
        // y axis is less than cylRadius. So we have to find a point
        // which is on the line, and match these conditions.

        // The equation for the y axis distances:
        // rayDirection.y * t - cylCenter.y = +- cylHeight
        // So t = (+-cylHeight + cylCenter.y) / rayDirection.y
        // About selecting the one we need:
        //  - Both has to be positive, or no intersection is visible.
        //  - If both are positive, we need the smaller one.
        float topT = (+cylHeight + cylCenter.y) / rayDirection.y;
        float bottomT = (-cylHeight + cylCenter.y) / rayDirection.y;
        if(topT > 0.0 && bottomT > 0.0){
            float t = min(topT,bottomT);

            // Now check for the x and z axis:
            // If the intersection is inside the circle (so the distance on the xz plain of the point,
            // and the center of circle is less than the radius), then its a point of the cylinder.
            // But we can't yet return because we might get a point from the the cylinder side
            // intersection that is closer to the camera.
            vec3 intersection = rayDirection * t;
            if( length(intersection.xz - cylCenter.xz) <= cylRadius ) {
                // The value we will (optianally) return is in camera space.
                circlePos = cameraRayDirection * t;
                // This one is ugly, but i didn't have better idea.
                circleNormal = length(circlePos - cameraCylCenter) <
                               length((circlePos - cameraCylCenter) + cylAxis) ? cylAxis : -cylAxis;
                circleIntersection = true;
            }
        }
    }


    // Find the intersection of the ray and the cylinder's side
    // The distance of the point and the y axis is sqrt(x^2 + z^2), which has to be equal to cylradius
    // (rayDirection.x*t - cylCenter.x)^2 + (rayDirection.z*t - cylCenter.z)^2 = cylRadius^2
    // So its a quadratic for t (A*t^2 + B*t + C = 0) where:
    // A = rayDirection.x^2 + rayDirection.z^2 - if this is 0, we won't get any intersection
    // B = -2*rayDirection.x*cylCenter.x - 2*rayDirection.z*cylCenter.z
    // C = cylCenter.x^2 + cylCenter.z^2 - cylRadius^2
    // It will give two results, we need the smaller one

    float A = rayDirection.x*rayDirection.x + rayDirection.z*rayDirection.z;
    if(A != 0.0) {
        float B = -2*(rayDirection.x*cylCenter.x + rayDirection.z*cylCenter.z);
        float C = cylCenter.x*cylCenter.x + cylCenter.z*cylCenter.z - cylRadius*cylRadius;

        float det = (B * B) - (4 * A * C);
        if(det >= 0.0){
            float sqrtDet = sqrt(det);
            float posT = (-B + sqrtDet)/(2*A);
            float negT = (-B - sqrtDet)/(2*A);

            float IntersectionT = min(posT, negT);
            vec3 Intersect = rayDirection * IntersectionT;

            if(abs(Intersect.y - cylCenter.y) < cylHeight){
                // Again it's in camera space
                sidePos = cameraRayDirection * IntersectionT;
                sideNormal = normalize(sidePos - cameraCylCenter);
                sideIntersection = true;
            }
        }
    }

    // Now get the results together:
    if(sideIntersection && circleIntersection){
        bool circle = length(circlePos) < length(sidePos);
        cameraPos = circle ? circlePos : sidePos;
        cameraNormal = circle ? circleNormal : sideNormal;
    } else if(sideIntersection){
        cameraPos = sidePos;
        cameraNormal = sideNormal;
    } else if(circleIntersection){
        cameraPos = circlePos;
        cameraNormal = circleNormal;
    } else
        discard;
}   
2 голосов
/ 24 мая 2013

В дополнение к ответу pygabriels я хочу поделиться автономной реализацией, использующей упомянутый код шейдера от Blaine Bell (PyMOL, Schrödinger, Inc.).

Подход, объясненный pygabriel, также может быть улучшен.Ограничительная рамка может быть выровнена таким образом, чтобы она всегда была обращена к зрителю.Только два лица видны максимум.Следовательно, требуется только 6 вершин (т.е. две грани, состоящие из 4 треугольников).

Смотрите рисунок здесь, поле (вектор направления) всегда обращено к зрителю: Изображение: Выровненная ограничительная рамка

Для исходного кода загрузите: исходный код самозванца для цилиндра

Код не распространяется на круглые заглавные буквы и ортогональные проекции.Он использует геометрический шейдер для генерации вершин.Вы можете использовать код шейдера в соответствии с лицензионным соглашением PyMOL.

2 голосов
/ 07 марта 2012

Из того, что я могу понять о работе, я бы интерпретировал ее следующим образом.

Цилиндр самозванца, если смотреть под любым углом, имеет следующие характеристики.

  1. Сверхуэто круг.Поэтому, учитывая, что вам никогда не нужно будет смотреть сверху на цилиндр, вам не нужно ничего визуализировать.
  2. Со стороны это прямоугольник.Пиксельный шейдер должен только вычислять освещенность как обычно.
  3. С любого другого угла, это прямоугольник (тот же самый, который вычислен на шаге 2), который изгибается.Его кривизна может быть смоделирована внутри пиксельного шейдера как кривизна верхнего эллипса.Эту кривизну можно рассматривать просто как смещение каждого «столбца» в текстурном пространстве в зависимости от угла обзора.Малую ось этого эллипса можно вычислить, умножив главную ось (толщину цилиндра) на коэффициент текущего угла обзора (угол / 90), предполагая, что 0 означает, что вы смотрите на цилиндр сбоку.

Fig 1. Углы обзора.Я только учел случай 0-90 в приведенной ниже математике, но остальные случаи тривиально отличаются.

Fig 2. Учитывая угол обзора (фи) и диаметр цилиндра (а), воткак шейдер должен деформировать ось Y в текстурном пространстве Y = b 'sin (phi).И b '= a * (фи / 90).Случаи phi = 0 и phi = 90 никогда не должны воспроизводиться.

Конечно, я не принял во внимание длину этого цилиндра - это будет зависеть от вашей конкретной проекции и не будет пространством изображенияпроблема.

...