Как получить дистанцию ​​и текстуры при рендеринге зеленого дракона - PullRequest
0 голосов
/ 30 июня 2019

Источник рендеринга зеленого дракона. Мой вопрос, как заставить работать расстояния клипа? Кроме того, текстуры не отображаются как ожидаемый результат. Любые идеи, что можно изменить в исходном коде, чтобы сделать программу, как ожидалось?

Обновление: с отличной помощью и превосходными ответами Rabbid76 работает дистанция обрезки и работает загрузка текстур! Спасибо.

Бонусный пример: clipdistance_torus_package.zip пример расстояния клипа с тором и текстурами!

Ожидаемый результат:

enter image description here

Файлы для запуска: clipdistance_dragon.zip

#!/usr/bin/python3

import sys
import time
import ctypes

fullscreen = True

sys.path.append("./shared")

from sbmloader import SBMObject    # location of sbm file format loader
from ktxloader import KTXObject

from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44, \
    scale, m3dLookAt, normalize

try:
    from OpenGL.GLUT import *
    from OpenGL.GL import *
    from OpenGL.GLU import *
    from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
    print ('''
    ERROR: PyOpenGL not installed properly.
        ''')
    sys.exit()

import numpy as np 
from math import cos, sin 
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]


myobject = SBMObject()

render_program = GLuint(0)
paused = False

class uniforms:
    proj_matrix = GLint(0)
    mv_matrix = GLint(0)
    clip_plane = GLint(0)
    clip_sphere = GLint(0)

uniform = uniforms()


def shader_load(filename, shader_type):
    result = GLuint(0)
    with open ( filename, "rb") as data:
        result = glCreateShader(shader_type)
        glShaderSource(result, data.read() )

    glCompileShader(result)
    if not glGetShaderiv(result, GL_COMPILE_STATUS):
        print( 'compile error:' )
        print( glGetShaderInfoLog(result) )
    return result

def link_from_shaders(shaders, shader_count, delete_shaders, check_errors=False):
    program = GLuint(0)
    program = glCreateProgram()

    for i in range(0, shader_count):
        glAttachShader(program, shaders[i])

    glLinkProgram(program)
    if not glGetProgramiv(program, GL_LINK_STATUS):
        print( 'link error:' )
        print( glGetProgramInfoLog(program) )

    if (delete_shaders):
        for i in range(0, shader_count):
            glDeleteShader(shaders[i])
    return program


def load_shaders():
    global render_program
    global uniform

    if (render_program):
        glDeleteProgram(render_program);

    shaders = [
        shader_load("render.vs.glsl", GL_VERTEX_SHADER),
        shader_load("render.fs.glsl", GL_FRAGMENT_SHADER)
    ]

    render_program = link_from_shaders(shaders, 2, True)

    uniform.proj_matrix = glGetUniformLocation(render_program, "proj_matrix");
    uniform.mv_matrix = glGetUniformLocation(render_program, "mv_matrix");
    uniform.clip_plane = glGetUniformLocation(render_program, "clip_plane");
    uniform.clip_sphere = glGetUniformLocation(render_program, "clip_sphere");

tex_dragon=None

class Scene:

    def __init__(self, width, height):
        global myobject, tex_dragon

        myobject.load("dragon.sbm");

        load_shaders()

        ktxobj = KTXObject()
        tex_dragon = ktxobj.ktx_load("pattern1.ktx")

    def display(self):
        global paused

        currentTime = time.time()

        black = [ 0.0, 0.0, 0.0, 0.0 ]
        one = 1.0

        last_time = 0.0
        total_time = 0.0

        if (not paused):
            total_time += (currentTime - last_time)
        last_time = currentTime

        f = total_time

        glClearBufferfv(GL_COLOR, 0, black)
        glClearBufferfv(GL_DEPTH, 0, one)

        glUseProgram(render_program)

        proj_matrix = (GLfloat * 16)(*identityMatrix)
        proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 0.1, 1000.0)

        T1 = (GLfloat * 16)(*identityMatrix)
        m3dTranslateMatrix44(T1, 0.0, 0.0, -15.0)

        RY = (GLfloat * 16)(*identityMatrix)
        m3dRotationMatrix44(RY, f * 0.34, 0.0, 1.0, 0.0)

        T2 = (GLfloat * 16)(*identityMatrix)
        m3dTranslateMatrix44(T2, 0.0, -4.0, 0.0)

        mv_matrix = (GLfloat * 16)(*identityMatrix)
        mv_matrix = m3dMultiply(T1, m3dMultiply(RY, T2))


        RX = (GLfloat * 16)(*identityMatrix)
        m3dRotationMatrix44(RX, f * 6.0, 1.0, 0.0, 0.0)

        RY = (GLfloat * 16)(*identityMatrix)
        m3dRotationMatrix44(RY, f * 7.3, 0.0, 1.0, 0.0)

        plane_matrix = (GLfloat * 16)(*identityMatrix)
        plane_matrix = m3dMultiply(RX , RY )

        plane = plane_matrix[0:4]
        plane[3] = 0
        plane = normalize(plane)

        clip_sphere = [sin(f * 0.7) * 3.0, cos(f * 1.9) * 3.0, sin(f * 0.1) * 3.0, cos(f * 1.7) + 2.5]

        glUniformMatrix4fv(uniform.proj_matrix, 1, GL_FALSE, proj_matrix)
        glUniformMatrix4fv(uniform.mv_matrix, 1, GL_FALSE, mv_matrix)
        glUniform4fv(uniform.clip_plane, 1, plane)
        glUniform4fv(uniform.clip_sphere, 1, clip_sphere)

        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CLIP_DISTANCE0)
        glEnable(GL_CLIP_DISTANCE1)

        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, tex_dragon)
        myobject.render()

        glutSwapBuffers()

    def reshape(self, width, height):
        self.width = width
        self.height = height

    def keyboard(self, key, x, y ):
        global fullscreen
        global paused

        print ('key:' , key)
        if key == b'\x1b': # ESC
            sys.exit()

        elif key == b'f' or key == b'F': #fullscreen toggle

            if (fullscreen == True):
                glutReshapeWindow(512, 512)
                glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
                fullscreen = False
            else:
                glutFullScreen()
                fullscreen = True

        elif key == b'p' or key == b'P':
            paused = not paused

        elif key == b'r' or key == b'R':
            pass 

        print('done')

    def init(self):
        pass

    def timer(self, blah):

        glutPostRedisplay()
        glutTimerFunc( int(1/60), self.timer, 0)
        time.sleep(1/60.0)


if __name__ == '__main__':
    start = time.time()

    glutInit()


    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)

    glutInitWindowSize(512, 512)

    w1 = glutCreateWindow('OpenGL SuperBible - Clip Distance')
    glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))

    fullscreen = False
    #glutFullScreen()

    scene = Scene(512,512)
    glutReshapeFunc(scene.reshape)
    glutDisplayFunc(scene.display)
    glutKeyboardFunc(scene.keyboard)

    glutIdleFunc(scene.display)
    #glutTimerFunc( int(1/60), scene.timer, 0)

    scene.init()

    glutMainLoop()

портировано в python из clipdistance.cpp , источник sbmobject.cpp на всякий случай sbmloader.py для вопроса о текстуре.

1 Ответ

1 голос
/ 30 июня 2019

код C ++ из примера

vmath::mat4 plane_matrix = vmath::rotate(f * 6.0f, 1.0f, 0.0f, 0.0f) *
                           vmath::rotate(f * 7.3f, 0.0f, 1.0f, 0.0f);

соответствует следующему коду Python

RX = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RX, f * 6.0, 1.0, 0.0, 0.0)

RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, f * 7.3, 0.0, 1.0, 0.0)

plane_matrix = (GLfloat * 16)(*identityMatrix)
plane_matrix = m3dMultiply(RX , RY)

Обратите внимание, вы должны поменять местами RX и RY в умножении матриц.


Ваши функции length и normalize могут работать только с векторами, которые имеют 3 компонента (x, y, z) . Для сравнения функция C ++ vmath::normalize из примера может обрабатывать векторы с 4 компонентами (x, y, z, w) .
Кроме того, обработка "деление на ноль" отсутствует в normalize.

Адаптируйте функции normalize и length для работы с любым векторным размером.
Если длина вектора равна 0, то все его компоненты равны 0. Для этого нет правильного решения, поэтому просто верните копию самого вектора.

def length(v):
    sum_sq = sum([s*s for s in v])
    return math.sqrt(sum_sq)
def normalize(v):
    l = length(v)
    if l == 0.0:
        return v[:]
    return [s/l for s in v]

Теперь вы можете портировать код C ++ из примера

vmath::vec4 plane = plane_matrix[0];
plane[3] = 0.0f;
plane = vmath::normalize(plane);

очень прямо к питону:

plane = plane_matrix[0:4]
plane[3] = 0
plane = normalize(plane)

Далее, есть проблема в модуле sbmloader.
Указан только массив координат вершин и текстурных координат. Нормальные векторы пропускаются.

Просто пропустите строку

if attrib.name=='position' or attrib.name=='map1':

чтобы исправить проблему:

for attrib_i, attrib in enumerate(vertex_attrib_chunk.attrib_data):

    #if attrib.name=='position' or attrib.name=='map1': 

    glVertexAttribPointer(attrib_i,
        attrib.size, attrib.type,
        GL_TRUE if (attrib.flags & SB6M_VERTEX_ATTRIB_FLAG_NORMALIZED) != 0 else GL_FALSE,
        attrib.stride, ctypes.c_void_p(int(attrib.data_offset)))
    glEnableVertexAttribArray(attrib_i)


Если вы хотите дополнительно обернуть текстуру в модель, вам нужно добавить атрибут координат текстуры в вершинный шейдер:

layout (location = 2) in vec2 tc;

И передать его выводом на следующий этап шейдера

out VS_OUT
{
    vec3 N;
    vec3 L;
    vec3 V;
    vec2 T;
} vs_out;
void main()
{
   // ...

   vs_out.T = tc;

   // ...  
}    

В фрагментный шейдер вы должны добавить равномерный сэмплер текстуры

layout (binding = 0) uniform sampler2D tex;

Прочитайте цвет формы текстуры

vec4 texColor = texture(tex, fs_in.T); 

Умножить выходной цвет на цвет текстуры

color = vec4(diffuse + specular + rim, 1.0) * texColor;

Фрагмент шейдера (заметьте, я изменил diffuse_albedo):

#version 420 core

// Output
layout (location = 0) out vec4 color;

// Input from vertex shader
in VS_OUT
{
    vec3 N;
    vec3 L;
    vec3 V;
    vec2 T;
} fs_in;

// Material properties
uniform vec3 diffuse_albedo = vec3(0.5);
uniform vec3 specular_albedo = vec3(0.7);
uniform float specular_power = 128.0;
uniform vec3 rim_color = vec3(0.1, 0.2, 0.2);
uniform float rim_power = 5.0;
layout (binding = 0) uniform sampler2D tex;

vec3 calculate_rim(vec3 N, vec3 V)
{
    float f = 1.0 - dot(N, V);

    f = smoothstep(0.0, 1.0, f);
    f = pow(f, rim_power);

    return f * rim_color;
}

void main(void)
{
    // Normalize the incoming N, L and V vectors
    vec3 N = normalize(fs_in.N);
    vec3 L = normalize(fs_in.L);
    vec3 V = normalize(fs_in.V);

    // Calculate R locally
    vec3 R = reflect(-L, N);

    // Compute the diffuse and specular components for each fragment
    vec3 diffuse = max(dot(N, L), 0.0) * diffuse_albedo;
    vec3 specular = pow(max(dot(R, V), 0.0), specular_power) * specular_albedo;
    vec3 rim = calculate_rim(N, V);

    // read color from the texture
    vec4 texColor = texture(tex, fs_in.T);

    // Write final color to the framebuffer
    color = vec4(diffuse + specular + rim, 1.0) * texColor;
}

Я рекомендую добавить шейдерную компиляцию и регистрацию ошибок ссылок:

glCompileShader(result)
if not glGetShaderiv(result, GL_COMPILE_STATUS):
    print( 'compile error:' )
    print( glGetShaderInfoLog(result) )
glLinkProgram(program)
if not glGetProgramiv(program, GL_LINK_STATUS):
    print( 'link error:' )
    print( glGetProgramInfoLog(program) )

Чтение текстуры при инициализации приложения

class Scene:

    def __init__(self, width, height):
        global myobject, tex_dragon

        myobject.load("dragon.sbm")

        load_shaders()

        ktxobj = KTXObject()
        tex_dragon = ktxobj.ktx_load("texture_file_name.ktx")

Привязать текстуру перед рисованием модели

glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, tex_dragon)
myobject.render()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...