Форум программистов, компьютерный форум, киберфорум
OpenGL
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.50/18: Рейтинг темы: голосов - 18, средняя оценка - 4.50
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73

Как сделать перемещение камеры в PyOpenGL?

28.08.2024, 20:55. Показов 6656. Ответов 65
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Мне помог один человек (в целях конфиденциальности я решил не писать его username) с изучением OpenGL с помощью Python. Я взял его код с крутящимся синим кубиком (фигур было больше, но для удобства я решил оставить одну). Код изучил я подробно, но возникла проблема с перемещением камеры клавишами: я не знаю как их сделать. Помогите, пожалуйста.
Вот код:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import ctypes
import math
 
import numpy as np
import pygame
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from pygame.locals import *
from pyrr import *
 
vertexShaderSource = """
    attribute vec3 aPosition;
    uniform mat4 uMvpMatrix;
 
    void main()
    {
        gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
    }
"""
 
fragmentShaderSource = """
    uniform vec3 uColor;
 
    void main()
    {
        gl_FragColor = vec4(uColor, 1.0);
    }
"""
 
def main():
    pygame.init()
    winWidth, winHeight = 800, 600
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
    
    #передаем глубину
    glEnable(GL_DEPTH_TEST)
 
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
    glUseProgram(program)
 
    # Create a cube
    #    v6----- v5
    #   /|      /|
    #  v1------v0|
    #  | |     | |
    #  | |v7---|-|v4
    #  |/      |/
    #  v2------v3
    vertPositions = np.array([
        -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, # v2-v3-v1-v0 front
        0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, # v3-v4-v0-v5 right
        -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, # v1-v0-v6-v5 up
        -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, # v7-v2-v6-v1 left
        -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, # v7-v4-v2-v3 down
        0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5 # v4-v7-v5-v6 back
    ], dtype=np.float32)
    vertPosBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
        vertPositions, GL_STATIC_DRAW)
 
    #работа с шейдерами    (достаем позицию фигуры)
    aPositionLocation = glGetAttribLocation(program, "aPosition")
    glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aPositionLocation)
 
    uColorLocation = glGetUniformLocation(program, "uColor")
    uMvpMatrixLocation = glGetUniformLocation(program, "uMvpMatrix")
 
    #перспектива
    projMatrix = matrix44.create_perspective_projection(fovy=45, aspect=winWidth/winHeight,
        near=0.1, far=1000)
    
    #положение камеры
    viewMatrix = matrix44.create_look_at(
        #позиция камеры
        eye=vector3.create(0, 20, 50),
        #место, в которое смотрит камера
        target=vector3.create(0, 0, 0),
        #направление вверх
        up=vector3.create(0, 1, 0))
    
    #объединяем положение камеры и перспективу
    projViewMatrix = matrix44.multiply(viewMatrix, projMatrix)
 
    #новая позиция фигуры
    position1 = vector3.create(0, 5, 0)
    
    #угол
    angle1 = 0
    
    #размер                 x    y   z
    scale1 = vector3.create(10, 10, 10)
    
    #цвет
    color1 = vector3.create(0.5, 0.5, 1)
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
 
        # передаем новою позицию куба
        translationMatrix = matrix44.create_from_translation(position1)
        
        #по какому углу будем изменять положение фигуры:
        #                                     x  (от этой буквы зависит перемещение фигуры по x, y, z)
        #                                    \/
        rotationMatrix = matrix44.create_from_x_rotation(math.radians(angle1))
        
        #передаем значения размера куба
        scaleMatrix = matrix44.create_from_scale(scale1)
        
        #передаем начальные координаты и координаты перемещения куба
        modelMatrix = matrix44.multiply(rotationMatrix, translationMatrix)
        
        #передаем координаты и размеры куба, объединяя их в одно целое
        modelMatrix = matrix44.multiply(scaleMatrix, modelMatrix)
        
        #объединяем матрицы вида, а также фигуру
        mvpMatrix = matrix44.multiply(modelMatrix, projViewMatrix)
        
        
        glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE, mvpMatrix)
        
        #передаем цвет фигуры
        glUniform3fv(uColorLocation, 1, color1)
        
        #отрисовываем фигуру
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 24)
        
        #во время цикла изменяем градус угла поворота куба
        angle1 += 1
 
        pygame.display.flip()
        pygame.time.wait(10)
 
main()
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
28.08.2024, 20:55
Ответы с готовыми решениями:

Как сделать плавное перемещение камеры вниз или вверх?
Здравствуйте!начну с того что я тупой и не могу самостоятельно написать скрипт на c#. Помогите пожалуйста, что мне нужно дополнить в...

Как сделать перемещение камеры в пространстве по типу FPS шутера
Как сделать перемещение камеры в пространстве по типу FPS шутера? Без тормозов. Даже без вращения, просто анимировать перемещение камеры к...

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

65
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
30.08.2024, 14:00
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Daniil_G Посмотреть сообщение
Как сделать перемещение камеры в PyOpenGL?
Удобнее иметь орбитальный облёт камеры, то есть камеру, которая может вращаться вокруг объекта с зажатой левой кнопкой мыши, перемещаться с помощью правой и отдаляться/приближаться с помощью колёсика мыши. То есть с таким же поведением, как камера в Blender. У меня есть небольшой пример на Qt C++. Можете переписать его на Python и PyGLM: orbit-controls-opengles2-qt6-cpp.zip У меня пока нет времени переписывать. Я собрал этот пример для веб: кликните, чтобы запустить в браузере



Если у вас появится желание параллельно изучать C++ и у вас Windows, то можете воспользоваться моим пошаговым руководством по началу использования SDL3, CMake, MinGW-w64. В нём сейчас следующие разделы:
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
30.08.2024, 14:58  [ТС]
Здравствуйте! Я скачал blender 2.67. Все работает
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
30.08.2024, 15:49
Теперь создайте новый проект с кодом:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# pip install Pygame PyOpenGL PyGLM numpy
 
import ctypes
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from pygame.locals import *
 
 
def main():
    pygame.init()
    winWidth, winHeight = 400, 300
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
 
    glClearColor(0.1, 0.3, 0.2, 1);
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
        glClear(GL_COLOR_BUFFER_BIT)
 
        pygame.display.flip()
        pygame.time.wait(10)
 
main()
Создайте квадрат в Blender, как я описывал по шагам в сообщении #18. Экспортируйте в файл "plane.dae". Задача - извлечь координаты вершин треугольников и массив нормалей из XML. Научитесь парсить XML на Python. Куб надо создавать не кодом, а загружать из Blender. Когда научитесь загружать квадрат, то загрузите и куб и любой другой объект, который сами создадите в Blender или найдёте в интернете. Когда извлечете массивы вершин квадрата и нормали, то покажите код.
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
31.08.2024, 11:22  [ТС]
Здравствуйте! Я сделал все по вашей инструкции, но не знаю, что делать после снятие галочки с include. Что надо ввести в верхнюю строчку "C:", чтобы сохранить проект на рабочий стол?
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
31.08.2024, 12:37
Цитата Сообщение от Daniil_G Посмотреть сообщение
не знаю, что делать после снятие галочки с include
После установки галочки "Selection Only" и снятия галочки "Include Shape Keys":

Название: c499ea3ec986e9707f3e6446b771ff1bbb4f2694.png
Просмотров: 79

Размер: 9.0 Кб

Забыл, написать, что надо прокрутить настройки экспорта ниже и удостовериться, что установлена галочка "Traingulate" и "Transformation Type" выставлено в значение "Matrix", по-моему, по умолчанию именно так и ничего менять здесь не надо:

Название: e4d2ea2d08dab1c0c4da4815663b1ba6dd72d06c.png
Просмотров: 79

Размер: 6.3 Кб

Что такое "Use Object Instances" я не знаю. Пусть установлено, как это есть по умолчанию. Далее, надо выбрать путь, куда сохранить файл - желательно выбрать путь к проекту, где будете писать код и ввести имя "plane.dae":



Ещё очень часто бывает удобно на панели слева, почти в начале настроек, нажать кнопку "Bookmarks" > "Add":

Название: 026507ca14726622eeeb411bf24bbf1605c542f3.png
Просмотров: 79

Размер: 1,017 байт

Тогда в следующий раз при экспорте отредактированного объекта, либо при экспорте нового объекта в тот же проект с кодом, не нужно будет указывать путь к проекту с кодом вручную. Достаточно будет на названия папки в разделе "Bookmarks":

Название: 515b1ceb8f6434d2424439ce96d2b28c9468789a.png
Просмотров: 79

Размер: 3.8 Кб

Теперь можно нажать кнопку "Export COLLADA" в правом вернем углу:

Название: 020c820b4ca94a0c949c7d19096aeb818c9f8d9b.png
Просмотров: 79

Размер: 1.6 Кб

Файл "plane.dae" будет сохранён в папку с кодом на Python. Теперь продолжайте изучать сообщение #18

Миниатюры
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
31.08.2024, 17:35  [ТС]
Спасибо, сохранить квадратик получилось, но я не очень понял из 18 сообщения, как достать координаты объекта (а также нормалей и т.д.) из файла
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
31.08.2024, 18:36
Двигаемся потихоньку шаг за шагом. Рядом с файлом "main.py" сейчас находится файл "plane.dae". Допишите код ниже, чтобы в нём читалось содержимое файла "plane.dae" и выводилось на экран. Если раньше не читали файл на Python, то нагуглите. Например, я в гугле набрал "read file content python". Одна из первых ссылок: https://www.w3schools.com/pyth... e_open.asp

Вот стартовый код в "main.py" куда нужно дописать чтение содержимого файла и вывод этого содержимого в консоль. Опубликуйте свой код полностью, то есть этот код с дописанным чтением файла и выводом его содержимого в консоль:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# pip install Pygame PyOpenGL PyGLM numpy
 
import ctypes
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from pygame.locals import *
 
 
def main():
    pygame.init()
    winWidth, winHeight = 400, 300
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
 
    glClearColor(0.1, 0.3, 0.2, 1);
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
        glClear(GL_COLOR_BUFFER_BIT)
 
        pygame.display.flip()
        pygame.time.wait(10)
 
main()
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
01.09.2024, 10:31  [ТС]
Лучший ответ Сообщение было отмечено 8Observer8 как решение

Решение

Здравствуйте! Квадрат получился. Я взял за основу ваш код с синим кубом и тенями, чтобы выделить работу нормалей.
Вот код:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import ctypes
import math
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from pygame.locals import *
 
vertexShaderSource = """
    attribute vec4 aPosition;
    attribute vec4 aNormal;
 
    uniform mat4 uNormalMatrix;
    uniform mat4 uMvpMatrix;
 
    const vec3 lightPosition = vec3(-10.0, 50.0, 30.0);
    const vec3 lightDirection = normalize(lightPosition);
    const float ambient = 0.3;
 
    varying float vDot;
 
    void main()
    {
        gl_Position = uMvpMatrix * aPosition;
        vec4 normal = uNormalMatrix * aNormal;
        vDot = max(dot(normalize(normal.xyz), lightDirection), ambient);
    }
"""
 
fragmentShaderSource = """
    uniform vec3 uColor;
 
    const vec3 lightColor = vec3(1.0, 1.0, 1.0);
 
    varying float vDot;
 
    void main()
    {
        vec3 diffuse = lightColor * uColor * vDot;
        gl_FragColor = vec4(diffuse, 1.0);
    }
"""
 
def main():
    pygame.init()
    winWidth, winHeight = 800, 600
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
    
    glEnable(GL_DEPTH_TEST)
 
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
    glUseProgram(program)
 
    # Create a cube
    #    v6----- v5
    #   /|      /|
    #  v1------v0|
    #  | |     | |
    #  | |v7---|-|v4
    #  |/      |/
    #  v2------v3
  
    f = open("plane.dae", "r")
    #print(f.read())
    doc = f.read()
    
    #ищем список координат
    spisok = doc.split('<float_array id="Plane-mesh-positions-array" count=')
    spisok = spisok[1].split('>')
    spisok = spisok[1].split('<')
    spisok = spisok[0].split()
    spisok = [float(i) for i in spisok]
    #print(spisok)
    #[1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]
    
    #ищем список нормалей
    spisok2 = doc.split('<float_array id="Plane-mesh-normals-array" count=')
    spisok2 = spisok2[1].split('>')
    spisok2 = spisok2[1].split('<')
    spisok2 = spisok2[0].split()
    spisok2 = [int(i) for i in spisok2]
    #print(spisok2)
    #[0, 0, 1, 0, 0, 1]
    
    #ищем список индекс
    spisok3 = doc.split('<p>')
    spisok3 = spisok3[1].split('<')
    spisok3 = spisok3[0].split()
    spisok3 = [int(i) for i in spisok3]
    #print(spisok3)
    #[1, 0, 0, 0, 2, 0, 3, 1, 1, 1, 2, 1]
    
    vertPositions = []
    normals = []
    k = 0
    for i in range(len(spisok3)):
        if k == 1:
            if spisok3[i] == 0:
                normals.append(spisok2[0])
                normals.append(spisok2[1])
                normals.append(spisok2[2])
            else:
                normals.append(spisok2[spisok3[i]*3])
                normals.append(spisok2[spisok3[i]*3+1])
                normals.append(spisok2[spisok3[i]*3+2])            
            k=0
        else:
            if spisok3[i] == 0:
                vertPositions.append(spisok[0])
                vertPositions.append(spisok[1])
                vertPositions.append(spisok[2])
            else:
                vertPositions.append(spisok[spisok3[i]*3])
                vertPositions.append(spisok[spisok3[i]*3+1])
                vertPositions.append(spisok[spisok3[i]*3+2])            
            k+=1        
    
            
    lenVertPositions = len(vertPositions)    
    vertPositions = np.array(vertPositions, dtype=np.float32)
    normals = np.array(normals, dtype=np.float32)    
    
    
    vertPosBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
        vertPositions, GL_STATIC_DRAW)
    aPositionLocation = glGetAttribLocation(program, "aPosition")
    glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aPositionLocation)
 
    normalBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, normalBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(normals) * 4,
        normals, GL_STATIC_DRAW)
    aNormalLocation = glGetAttribLocation(program, "aNormal")
    glVertexAttribPointer(aNormalLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aNormalLocation)
 
    uColorLocation = glGetUniformLocation(program, "uColor")
    uNormalMatrixLocation = glGetUniformLocation(program, "uNormalMatrix")
    uMvpMatrixLocation = glGetUniformLocation(program, "uMvpMatrix")
 
    projMatrix = glm.perspective(math.radians(45), winWidth/winHeight, 0.1, 1000)
 
    cameraSpeed = 1
    eye = glm.vec3(0, 20, 50)
    target = glm.vec3(0, 0, 0)
    up = glm.vec3(0, 1, 0)
 
    viewMatrix = glm.lookAt(
        glm.vec3(0, 20, 50), # position
        glm.vec3(0, 0, 0), # target
        glm.vec3(0, 1, 0)) # up
    projViewMatrix = projMatrix * viewMatrix
 
    position1 = glm.vec3(0, 5, 0)
 
    angle1 = 0
 
    scale1 = glm.vec3(10, 10, 10)
    
    color1 = glm.vec3(0.5, 0.5, 1)
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
 
        # Cube
        # Model matrix
        modelMatrix = glm.translate(glm.mat4(1), position1)
        modelMatrix = glm.rotate(modelMatrix, math.radians(angle1), glm.vec3(1, 0, 0))
        modelMatrix = glm.scale(modelMatrix, scale1)
        # Normal matrix
        normalMatrix = glm.inverse(modelMatrix)
        normalMatrix = glm.transpose(normalMatrix)
        glUniformMatrix4fv(uNormalMatrixLocation, 1, GL_FALSE, glm.value_ptr(normalMatrix))
        # MVP-matrix
        mvpMatrix = projViewMatrix * modelMatrix
        glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE, glm.value_ptr(mvpMatrix))
        # Color
        glUniform3fv(uColorLocation, 1, glm.value_ptr(color1))
        glDrawArrays(GL_TRIANGLES, 0, lenVertPositions)
        angle1 += 1
 
        pygame.display.flip()
        pygame.time.wait(10)
 
main()
1
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
01.09.2024, 13:25
Я не знаю, как решить проблему с кодировкой. Когда из консоли запускаю ваши программы с русскими комментариями, то всегда возникает ошибки при запуске на Windows 10 из CMD и из Far:

Code
1
2
3
>python main.py
  File "main.py", line 73
SyntaxError: Non-UTF-8 code starting with '\xe8' in file main.py on line 73, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
Комментарии отображаются в Sublime Text 4 кракозябами:

Python
1
2
3
4
5
6
7
8
    #èùåì ñïèñîê êîîðäèíàò
    spisok = doc.split('<float_array id="Plane-mesh-positions-array" count=')
    spisok = spisok[1].split('>')
    spisok = spisok[1].split('<')
    spisok = spisok[0].split()
    spisok = [float(i) for i in spisok]
    #print(spisok)
    #[1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]
Вы молодец, что написали код парсинга XML без использования библиотек. Самое главное, вы поняли, как хранятся данные в Collada. Вращающаяся плоскость выводится. Но при попытке распарсить куб возникает ошибка:

Code
1
2
3
  File "main.py", line 74, in main
    spisok = spisok[1].split('>')
IndexError: list index out of range
Она возникает из-за того, что теперь имя объекта "Cube", а не "Plane". А если куб в Blender удалить и создать новый, то имя при экспорте будет "Cube_001", а если ещё раз удалить и создать куб, то "Cube_002" и т.д. На данный момент, эта проблема решается открытием файла ".dae" после экспорта и просмотром в нём какое там имя объекта и заменой этим именем старого имени в коде программы. Я экспортировал куб в файл "cube.dae", поэтому заменил "plane.dae" на "cube.dae" здесь:

Python
1
2
3
    f = open("cube.dae", "r")
    #print(f.read())
    doc = f.read()
Теперь "id" имеет значение "Cube_002-mesh-positions-array", а не "Plane-mesh-positions-array", поэтому я заменил "Plane" на "Cube_002":

Python
1
2
3
4
5
6
7
    spisok = doc.split('<float_array id="Plane-mesh-positions-array" count=')
    spisok = spisok[1].split('>')
    spisok = spisok[1].split('<')
    spisok = spisok[0].split()
    spisok = [float(i) for i in spisok]
    #print(spisok)
    #[1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]
Куб выводится! Мало того, выводится любой объект созданный в Blender - об этом ниже. Вы очень круто справились! Без привлечения библиотек парсинга XML типа встроенной в Python, либо без pugixml Сделали просто и красиво! Молодец!



Мало того, ваш код выводит сферу (Shift+A > "Mesh" > "UV Sphere"). Опять же просто меняем в трёх местах в программе:
  • имя файла на "sphere.dae"
  • Смотрим в "sphere.dae" какое там имя объекта и меняем его в "id" массива вершин
  • Меняем имя в массиве нормалей

Кстати, возникает ошибка на 85 строке при импорте сферы:

Python
1
2
3
  File "main.py", line 85, in <listcomp>
    spisok2 = [int(i) for i in spisok2]
ValueError: invalid literal for int() with base 10: '-0.7707797'
Потому что нормали должны быть типа "float", а не "int", но это легко исправляется:

Python
1
spisok2 = [float(i) for i in spisok2]


Чтобы сфера была гладкой, надо в Blender слева на T-панели, которая открывается и закрывается с помощью клавиши "T" найти опцию, состоящую из двух кнопок "Smooth" и "Flat". Надо нажать "Smooth" (можно будет вернуться обратно к первоначальной сфере, нажав "Flat"), нормали будут усреднены:



Надо всегда следить, чтобы имя объекта в ".dae" совпрадало с именем объекта в программе.

Голова обезьяны: "Shift+A" > "Mesh" > "Monkey":



Можно нажать кнопку "Smooth" на T-панели:



Миниатюры
1
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
01.09.2024, 13:33
На этом нельзя останавливаться. Надо делать следующий шаг. Выведите разные стандартные объекты из Blender: куб, сфера, конус, цилиндр, голова обезьяны и т.д. одновременно. Пусть какие-то из них вращаются вокруг разных осей. Самое главное, что бы они были на одной сцене, но из разных файлов. Прикрепите архив:



Миниатюры
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
02.09.2024, 18:41  [ТС]
Здравствуйте! Я выполнял ваше задание, решил пока просто отрисовать все объекты (без угла поворота). У меня возникла проблема, что я не вижу фигуры: квадрат, обезьянку, куб и шар

вот исходники в формате txt, т.к. формат ___.dae у меня выдавал ошибку, из-за чего я решил поменять в коде формат на txt. А также лучше скачайте zip папку снизу, там находятся все фигуры.

Вот код:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import ctypes
import math
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from pygame.locals import *
 
vertexShaderSource = """
    attribute vec4 aPosition;
    attribute vec4 aNormal;
 
    uniform mat4 uNormalMatrix;
    uniform mat4 uMvpMatrix;
 
    const vec3 lightPosition = vec3(-10.0, 50.0, 30.0);
    const vec3 lightDirection = normalize(lightPosition);
    const float ambient = 0.3;
 
    varying float vDot;
 
    void main()
    {
        gl_Position = uMvpMatrix * aPosition;
        vec4 normal = uNormalMatrix * aNormal;
        vDot = max(dot(normalize(normal.xyz), lightDirection), ambient);
    }
"""
 
fragmentShaderSource = """
    uniform vec3 uColor;
 
    const vec3 lightColor = vec3(1.0, 1.0, 1.0);
 
    varying float vDot;
 
    void main()
    {
        vec3 diffuse = lightColor * uColor * vDot;
        gl_FragColor = vec4(diffuse, 1.0);
    }
"""
 
def main():
    pygame.init()
    winWidth, winHeight = 800, 600
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
    
    glEnable(GL_DEPTH_TEST)
 
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
    glUseProgram(program)
 
    # Create a cube
    #    v6----- v5
    #   /|      /|
    #  v1------v0|
    #  | |     | |
    #  | |v7---|-|v4
    #  |/      |/
    #  v2------v3
    names = ('untitled.txt', 'cube.txt', 'plane.txt', 'monkey.txt')
    vertPositions = []
    normals = []
  
    def models3d():
        for i in range(len(names)):
            f = open(names[i], "r")
            #print(f.read())
            doc = f.read()
            #
            spisok = doc.split('<float_array id=')
            spisok = spisok[1].split('-mesh-positions-array" count=')
            spisok = spisok[1].split('>')
            spisok = spisok[1].split('<')
            spisok = spisok[0].split()
            spisok = [float(i) for i in spisok]
            #print(len(spisok))
            #[1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]
            #
            spisok2 = doc.split('<float_array id=')
            spisok2 = spisok2[2].split('-mesh-normals-array" count=')
            spisok2 = spisok2[1].split('>')
            spisok2 = spisok2[1].split('<')
            spisok2 = spisok2[0].split()
            spisok2 = [float(i) for i in spisok2]
            #print(spisok2)
            #[0, 0, 1, 0, 0, 1]
            spisok3 = doc.split('<p>')
            spisok3 = spisok3[1].split('<')
            spisok3 = spisok3[0].split()
            spisok3 = [int(i) for i in spisok3]
            #print(spisok3)
            #[1, 0, 0, 0, 2, 0, 3, 1, 1, 1, 2, 1]
            vertPositions2 = []
            normals2 = []
            k = 0
            for i in range(len(spisok3)):
                if k == 1:
                    if spisok3[i] == 0:
                        normals2.append(spisok2[0])
                        normals2.append(spisok2[1])
                        normals2.append(spisok2[2])
                    else:
                        normals2.append(spisok2[spisok3[i]*3])
                        normals2.append(spisok2[spisok3[i]*3+1])
                        normals2.append(spisok2[spisok3[i]*3+2])            
                    k=0
                else:
                    if spisok3[i] == 0:
                        vertPositions2.append(spisok[0])
                        vertPositions2.append(spisok[1])
                        vertPositions2.append(spisok[2])
                    else:
                        vertPositions2.append(spisok[spisok3[i]*3])
                        vertPositions2.append(spisok[spisok3[i]*3+1])
                        vertPositions2.append(spisok[spisok3[i]*3+2])            
                    k+=1
            vertPositions.append(vertPositions2)
            normals.append(normals2)
        return vertPositions, normals
    lenVertPositions = len(vertPositions)
    vertPositions = np.array([vertPositions
    ], dtype=np.float32)
    vertPosBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
        vertPositions, GL_STATIC_DRAW)
    aPositionLocation = glGetAttribLocation(program, "aPosition")
    glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aPositionLocation)
 
    normals = np.array([normals
    ], dtype=np.float32)
    normalBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, normalBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(normals) * 4,
        normals, GL_STATIC_DRAW)
    aNormalLocation = glGetAttribLocation(program, "aNormal")
    glVertexAttribPointer(aNormalLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aNormalLocation)
 
    uColorLocation = glGetUniformLocation(program, "uColor")
    uNormalMatrixLocation = glGetUniformLocation(program, "uNormalMatrix")
    uMvpMatrixLocation = glGetUniformLocation(program, "uMvpMatrix")
 
    projMatrix = glm.perspective(math.radians(45), winWidth/winHeight, 0.1, 1000)
 
    cameraSpeed = 1
    eye = glm.vec3(10, 10, 10)
    target = glm.vec3(0, 0, 0)
    up = glm.vec3(0, 1, 0)
 
    viewMatrix = glm.lookAt(
        eye, # position
        target, # target
        up) # up
    projViewMatrix = projMatrix * viewMatrix
 
    #cubes 1
    position1 = glm.vec3(0, 5, 0)
    angle1 = 0
    scale1 = glm.vec3(10, 10, 10)
    color1 = glm.vec3(0.5, 0.5, 1)
 
    keys = { "left": False, "right": False, "up": False, "down": False }
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            eye[0] -= 0.5
            target[0] -= 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix
        if keys["right"]:
            eye[0] += 0.5
            target[0] += 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix
        if keys["up"]:
            eye[2] -= 0.5
            target[2] -= 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix            
        if keys["down"]:
            eye[2] += 0.5
            target[2] += 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix            
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
 
        # Cube
        # Model matrix
        modelMatrix = glm.translate(glm.mat4(1), position1)
        modelMatrix = glm.rotate(modelMatrix, math.radians(angle1), glm.vec3(1, 0, 0))
        modelMatrix = glm.scale(modelMatrix, scale1)
        # Normal matrix
        normalMatrix = glm.inverse(modelMatrix)
        normalMatrix = glm.transpose(normalMatrix)
        glUniformMatrix4fv(uNormalMatrixLocation, 1, GL_FALSE, glm.value_ptr(normalMatrix))
        # MVP-matrix
        mvpMatrix = projViewMatrix * modelMatrix
        glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE, glm.value_ptr(mvpMatrix))
        # Color
        glUniform3fv(uColorLocation, 1, glm.value_ptr(color1))
        glDrawArrays(GL_TRIANGLES, 0, lenVertPositions)
        #angle1 += 1
 
        pygame.display.flip()
        pygame.time.wait(10)
 
main()
Вложения
Тип файла: txt cube.txt (2.7 Кб, 0 просмотров)
Тип файла: txt plane.txt (2.4 Кб, 0 просмотров)
Тип файла: zip models3d.zip (37.7 Кб, 5 просмотров)
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
02.09.2024, 20:06
Я попробовал оставить один объект в массиве names и добавил вывод имени в models3d()

Python
1
2
3
4
5
6
7
8
9
10
11
12
    #names = ('untitled.txt', 'cube.txt', 'plane.txt', 'monkey.txt')
    names = ('plane.txt',)
    vertPositions = []
    normals = []
  
    def models3d():
        for i in range(len(names)):
            f = open(names[i], "r")
            #print(f.read())
            doc = f.read()
            #
            print(names[i])
, но оно не вывелось, потому что models3d() нигде не вызывается. Я оставил в массиве только "plane", так как с ней проще отлаживать. После вызова models3d() решил проверить сколько вершин считалось:

Python
1
2
3
    vertPositions, normals = models3d()
    lenVertPositions = len(vertPositions)
    print(lenVertPositions)
Выводит 1 (единицу). Оказывается вы используете метод "append". Я заменил на "extend":

Python
1
2
vertPositions.extend(vertPositions2)
normals.extend(normals2)
Вот здесь убрал квадратные скобки вокруг "vertPositions" и "normals":

Python
1
2
3
vertPositions = np.array(vertPositions, dtype=np.float32)
# ...
normals = np.array(normals, dtype=np.float32)
Теперь плоскость выводится. Я вернул остальные объекты:

Python
1
2
names = ('untitled.txt', 'cube.txt', 'plane.txt', 'monkey.txt')
# names = ('plane.txt',)
Все объекты выводятся в нулевой точке координат. Видно, как ухо обезьяны торчит из куба:



Это неверный способ - добавить все вершины треугольников всех объектов в один буфер вершин и все нормали в один буфер нормалей. Надо для каждого объекта создать свой буфер вершин и свой буфер нормалей и делать привязку к буферам вершин и нормалей одного объекта перед рисованием, то есть перед вызовом glDrawArrays(). Для каждого объекта отправить свою MVP-матрицу и вызвать свой glDrawArrays() со своим количеством вершин. Перед каждым glDrawArrays нужно вызывать glBindBuffer(), glVertexAttribPointer() и glEnableVertexAttribArray()

Это ваш код, который рисует все объекты в нулевой точке координат:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import ctypes
import math
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from pygame.locals import *
 
vertexShaderSource = """
    attribute vec4 aPosition;
    attribute vec4 aNormal;
 
    uniform mat4 uNormalMatrix;
    uniform mat4 uMvpMatrix;
 
    const vec3 lightPosition = vec3(-10.0, 50.0, 30.0);
    const vec3 lightDirection = normalize(lightPosition);
    const float ambient = 0.3;
 
    varying float vDot;
 
    void main()
    {
        gl_Position = uMvpMatrix * aPosition;
        vec4 normal = uNormalMatrix * aNormal;
        vDot = max(dot(normalize(normal.xyz), lightDirection), ambient);
    }
"""
 
fragmentShaderSource = """
    uniform vec3 uColor;
 
    const vec3 lightColor = vec3(1.0, 1.0, 1.0);
 
    varying float vDot;
 
    void main()
    {
        vec3 diffuse = lightColor * uColor * vDot;
        gl_FragColor = vec4(diffuse, 1.0);
    }
"""
 
def main():
    pygame.init()
    winWidth, winHeight = 800, 600
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
    
    glEnable(GL_DEPTH_TEST)
    glClearColor(0.2, 0.2, 0.2, 1)
 
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
    glUseProgram(program)
 
    # Create a cube
    #    v6----- v5
    #   /|      /|
    #  v1------v0|
    #  | |     | |
    #  | |v7---|-|v4
    #  |/      |/
    #  v2------v3
    names = ('untitled.txt', 'cube.txt', 'plane.txt', 'monkey.txt')
    # names = ('plane.txt',)
    vertPositions = []
    normals = []
  
    def models3d():
        for i in range(len(names)):
            f = open(names[i], "r")
            #print(f.read())
            doc = f.read()
            spisok = doc.split('<float_array id=')
            spisok = spisok[1].split('-mesh-positions-array" count=')
            spisok = spisok[1].split('>')
            spisok = spisok[1].split('<')
            spisok = spisok[0].split()
            spisok = [float(i) for i in spisok]
            # print(len(spisok))
            #[1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]
            #
            spisok2 = doc.split('<float_array id=')
            spisok2 = spisok2[2].split('-mesh-normals-array" count=')
            spisok2 = spisok2[1].split('>')
            spisok2 = spisok2[1].split('<')
            spisok2 = spisok2[0].split()
            spisok2 = [float(i) for i in spisok2]
            # print(spisok2)
            #[0, 0, 1, 0, 0, 1]
            spisok3 = doc.split('<p>')
            spisok3 = spisok3[1].split('<')
            spisok3 = spisok3[0].split()
            spisok3 = [int(i) for i in spisok3]
            # print(spisok3)
            #[1, 0, 0, 0, 2, 0, 3, 1, 1, 1, 2, 1]
            vertPositions2 = []
            normals2 = []
            k = 0
            for i in range(len(spisok3)):
                if k == 1:
                    if spisok3[i] == 0:
                        normals2.append(spisok2[0])
                        normals2.append(spisok2[1])
                        normals2.append(spisok2[2])
                    else:
                        normals2.append(spisok2[spisok3[i]*3])
                        normals2.append(spisok2[spisok3[i]*3+1])
                        normals2.append(spisok2[spisok3[i]*3+2])            
                    k=0
                else:
                    if spisok3[i] == 0:
                        vertPositions2.append(spisok[0])
                        vertPositions2.append(spisok[1])
                        vertPositions2.append(spisok[2])
                    else:
                        vertPositions2.append(spisok[spisok3[i]*3])
                        vertPositions2.append(spisok[spisok3[i]*3+1])
                        vertPositions2.append(spisok[spisok3[i]*3+2])            
                    k+=1
            vertPositions.extend(vertPositions2)
            normals.extend(normals2)
        return vertPositions, normals
    vertPositions, normals = models3d()
    lenVertPositions = len(vertPositions)
    vertPositions = np.array(vertPositions, dtype=np.float32)
    vertPosBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
        vertPositions, GL_STATIC_DRAW)
    aPositionLocation = glGetAttribLocation(program, "aPosition")
    glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aPositionLocation)
 
    normals = np.array(normals, dtype=np.float32)
    normalBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, normalBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(normals) * 4,
        normals, GL_STATIC_DRAW)
    aNormalLocation = glGetAttribLocation(program, "aNormal")
    glVertexAttribPointer(aNormalLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aNormalLocation)
 
    uColorLocation = glGetUniformLocation(program, "uColor")
    uNormalMatrixLocation = glGetUniformLocation(program, "uNormalMatrix")
    uMvpMatrixLocation = glGetUniformLocation(program, "uMvpMatrix")
 
    projMatrix = glm.perspective(math.radians(45), winWidth/winHeight, 0.1, 1000)
 
    cameraSpeed = 1
    eye = glm.vec3(50, 50, 50)
    target = glm.vec3(0, 0, 0)
    up = glm.vec3(0, 1, 0)
 
    viewMatrix = glm.lookAt(
        eye, # position
        target, # target
        up) # up
    projViewMatrix = projMatrix * viewMatrix
 
    #cubes 1
    position1 = glm.vec3(0, 5, 0)
    angle1 = 0
    scale1 = glm.vec3(10, 10, 10)
    color1 = glm.vec3(0.5, 0.5, 1)
 
    keys = { "left": False, "right": False, "up": False, "down": False }
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            eye[0] -= 0.5
            target[0] -= 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix
        if keys["right"]:
            eye[0] += 0.5
            target[0] += 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix
        if keys["up"]:
            eye[2] -= 0.5
            target[2] -= 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix            
        if keys["down"]:
            eye[2] += 0.5
            target[2] += 0.5
            viewMatrix = glm.lookAt(eye, target, up)
            projViewMatrix = projMatrix * viewMatrix            
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
 
        # Cube
        # Model matrix
        modelMatrix = glm.translate(glm.mat4(1), position1)
        modelMatrix = glm.rotate(modelMatrix, math.radians(angle1), glm.vec3(1, 0, 0))
        modelMatrix = glm.scale(modelMatrix, scale1)
        # Normal matrix
        normalMatrix = glm.inverse(modelMatrix)
        normalMatrix = glm.transpose(normalMatrix)
        glUniformMatrix4fv(uNormalMatrixLocation, 1, GL_FALSE, glm.value_ptr(normalMatrix))
        # MVP-matrix
        mvpMatrix = projViewMatrix * modelMatrix
        glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE, glm.value_ptr(mvpMatrix))
        # Color
        glUniform3fv(uColorLocation, 1, glm.value_ptr(color1))
        glDrawArrays(GL_TRIANGLES, 0, lenVertPositions)
        #angle1 += 1
 
        pygame.display.flip()
        pygame.time.wait(10)
 
main()
Миниатюры
1
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
02.09.2024, 23:53
Суть в том, что у каждого объекта есть свои атрибуты: вершины треугольников, нормали, текстурные координаты и т.д. В вершинном шейдере так и пишется "attribute". Вершинный шейдер берёт значения атрибутов из буферов, куда были загружены массивы данных. Чтобы шейдер понял из каких буферов брать данные и как эти данные хранятся нужно сделать привязку буферов, активацию переменной атрибута и настройку атрибута:

Например, надо рисовать куб. Массивы вершин треугольников были загружены в буфер cubeVertPosBuffer, а массив нормалей был загружен в буфер cubeNormalBuffer. Перед вызовом glDrawArrays(GL_TRIANGLES, 0, amountOfCubeVertices) надо сначала сделать привязку к cubeVertPosBuffer, активировать атрибут и настроить атрибут, чтобы шейдер понимал, что данные вершин занимают 3 позиции, то есть (x, y, z), так как в вершинном шейдере vec3, а данные в атрибуте имеют тип float:
Python
1
2
3
glBindBuffer(GL_ARRAY_BUFFER, cubeVertPosBuffer)
glEnableVertexAttribArray(aPositionLocation)
glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
Делаем привязку к буферу нормалей, активируем атрибут и делаем настройку атрибута:
Python
1
2
3
glBindBuffer(GL_ARRAY_BUFFER, cubeNormalBuffer)
glEnableVertexAttribArray(aNormalLocation)
glVertexAttribPointer(aNormalLocation, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
Задаём modelMatrix, projViewMatrix и объединяем это в одну mvpMatrix и передаём в шейдер, чтобы умножить mvpMatrix на каждый атрибут позиции вершины треугольника и тем самым задать поворот, размер, позицию, вид и проекцию. Когда это всё готово, то запускаются шейдеры после вызова glDrawArrays(GL_TRIANGLES, 0, amountOfCubeVertices). Вершинный шейдер будет выполнятся не последовательно для каждой вершины треугольника, а параллельно в разных потоках.

Далее, нужно повторить туже самую процедуру для других объектов. Сделать привязку к буферу вершин, например, конуса, настроить атрибуты, сделать привязку к буферу нормалей конуса, настроить атрибуты, передать MVP-Матрицу и вызвать glDrawArrays(GL_TRIANGLES, 0, amountOfConeVertices). Только передать уже не число вершин куба, а число вершин конуса. Кстати, число вершин это не len(vertPositions), а len(vertPositions)//3, потому что vertPositions хранит x, y, z, x1, y1, z1 и т.д. то есть на одну вершину приходится три числа.
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
03.09.2024, 01:35
Добавил в ваш пример, который я исправил в сообщении 32, вращение камерой зажатой левой мышкой, перемещение камеры зажатой правой кнопкой мыши и приближение и отдаление камеры с помощью колёсика мыши. Я просто перевёл пример zayats80888 из Qt C++ на Python из этого сообщения: Вращение камеры мышкой после её перемещения
Вложения
Тип файла: zip models3d.zip (39.3 Кб, 8 просмотров)
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
04.09.2024, 15:11
Сделайте сначала другое задание в 2D (с ортогональной проекцией). Нарисуйте в одном окне три объекта:
  • красный треугольник, повёрнутый на 20 градусом по часовой стрелке
  • Селёный квадрат, повёрнутый на 30, против часовой
  • Синий прямоугольник, повёрнутый на 50 градусов по часовой стрелке
За основу возьмите пример из следующего сообщения, скопируйте его и дополняйте: Как сделать изменение координат (или анимацию) в PyOpenGL?
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
05.09.2024, 14:32
Исправлю описку

Добавлено через 42 секунды
Не получилось. Написал "Селёный"

Добавлено через 1 минуту
Я думаю, что нужно создать отдельный класс для рисования объекта и предавать ему массив вершин треугольников. Хранить в этом классе вершинные буферы и т.д.
0
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
05.09.2024, 18:06  [ТС]
Здравствуйте! У меня появилось свободное время, чтобы мог по программировать и посмотреть данный форум. Я прочитал ваше сообщение на счет буфера. Я не очень понял, как можно осуществить создание нескольких буферов. Я могу сделать отрисовку именных координат, с созданием разных мвп матриц, но я не очень понял, как будет работать код при нескольких буферов координат и нормалей.
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
05.09.2024, 20:08
Daniil_G, это будет понятно, когда вы попытаетесь добавить ещё один объект. Например, есть этот код, где треугольник двигается с помощью клавиш. Добавьте в него квадрат. Переименуйте этот массив и буфер в буфер треугольника, вот так:

Python
1
2
3
4
5
6
7
8
    # Задаём три вершины треугольника против часовой стрелки
    vertPositionsForTriangle = np.array([
        -0.5, -0.5,
        0.5, -0.5,
        0, 0.5
    ], dtype=np.float32)
    # Создание буфера в оперативной памяти видеокарты
    vertPosBufferForTriangle = glGenBuffers(1)
Добавьте буфер для квадрата:

Python
1
2
3
4
5
6
7
8
    # Задаём три вершины треугольника против часовой стрелки
    vertPositionsForSquare = np.array([
        -0.5, -0.5,
        0.5, -0.5,
        0, 0.5
    ], dtype=np.float32)
    # Создание буфера в оперативной памяти видеокарты
    vertPosBufferForSquare = glGenBuffers(1)
Задача в том, чтобы добавить ещё один объект - Square, то есть квадрат. Надо принцип всегда в голове держать, что шейдер работает с привязанным буфером, куда вы загрузили массив данных.

Добавлено через 1 час 44 минуты
Алгоритм подготовки и рисования нескольких объектов следующий:
  • Создаём массив координат вершин треугольников
  • Отправляем команду видеокарте о создании пустого буфера: buffer1 = glGenBuffers(1)
  • Отправляем команду видеокарте о привязке к указанному буферу: glBindBuffer(GL_ARRAY_BUFFER, buffer1)
  • Копируем данные в буфер: glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4, vertPositions, GL_STATIC_DRAW)
  • Создаём две общие матрицы для всех объектов: матрицу вида (камеру) и матрицу проекции (если 2D, то создаём ортогональную, если 3D - создаём перспективную)
  • Создаём матрицу модели, которая хранит в себе размер объекта, его положение в пространстве и его поворот

Также создаём буферы для других объектов. Перед тем как рисовать, то есть перед вызовом glDrawArray() нужно сделать привязку к буферу (или буферам: вершин, нормалей, текстурных координат и т.д.), чтобы вершинный шейдер знал из каких буферов загружать данные, сделать настройку атрибутов, передать матрицу MVP вершинный шейдер, передать цвет в фрагментный шейдер. После этих подготовок нужно вызвать glDrawArray() и тогда запустятся вершинные шейдеры, которые параллельно в разных потоках будут обрабатывать данные из буферов. Например, брать вершины треугольников, умножать матрицу MVP на них.
0
9949 / 2949 / 497
Регистрация: 05.10.2013
Сообщений: 8,025
Записей в блоге: 242
06.09.2024, 02:27
Так выглядит решение задачи по созданию общего класса для всех моделей. В примере ниже есть один общий класс с именем Model в отдельном файле "model.py". В конструктор этого класса передаются следующие данные и сохраняются. Написал в комментариях, что происходит:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import glm
import numpy as np
from OpenGL.GL import *
 
 
class Model:
 
    def __init__(self, vertPositions, program, position, angle, scale, color):
        # Save the input variables
        self.program = program
        self.position = position
        self.angle = angle
        self.scale = scale
        self.color = color
 
        # Amount of vertices
        self.amountOfVertices = len(vertPositions) // 2
 
        # Covert a Python list to a numpy array
        vertPositions = np.array(vertPositions, dtype=np.float32)
 
        # Create the vertex position buffer
        self.vertPosBuffer = glGenBuffers(1)
        # Bind the buffer
        glBindBuffer(GL_ARRAY_BUFFER, self.vertPosBuffer)
        # Copy data to the buffer
        glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
            vertPositions, GL_STATIC_DRAW)
 
        # Get locations of the shader variables
        self.aPositionLocation = glGetAttribLocation(self.program, "aPosition")
        self.uColorLocation = glGetUniformLocation(self.program, "uColor")
        self.uMvpMatrixLocation = glGetUniformLocation(self.program, "uMvpMatrix")
В это классе ещё есть метод draw, принимающий общую для всех моделей projView-матрицу:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    def draw(self, projViewMatrix):
        glUseProgram(self.program)
        # Bind the vertex position buffer
        glBindBuffer(GL_ARRAY_BUFFER, self.vertPosBuffer)
 
        # Setting up the aPosition attribute
        glEnableVertexAttribArray(self.aPositionLocation)
        glVertexAttribPointer(self.aPositionLocation, 2, GL_FLOAT, GL_FALSE,
            0, ctypes.c_void_p(0))
 
        # Send MVP-matrix to the vertex shader
        # Translate
        modelMatrix = glm.translate(glm.mat4(1), self.position)
        # Rotate
        modelMatrix = glm.rotate(modelMatrix, glm.radians(self.angle),
            glm.vec3(0, 0, 1))
        # Scale
        modelMatrix = glm.scale(modelMatrix, self.scale)
        mvpMatrix = projViewMatrix * modelMatrix
        glUniformMatrix4fv(self.uMvpMatrixLocation, 1, GL_FALSE,
            glm.value_ptr(mvpMatrix))
 
        # Send a color to the fragment shader
        glUniform3fv(self.uColorLocation, 1, glm.value_ptr(self.color))
 
        # Draw the model
        glDrawArrays(GL_TRIANGLES, 0, self.amountOfVertices)
Шейдеры я убрал в отдельные файлы "color.vert" и "color.frag", которые хранятся в папке "assets/shaders":

color.vert

glSlang
1
2
3
4
5
6
7
attribute vec2 aPosition;
uniform mat4 uMvpMatrix;
 
void main()
{
    gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
}
color.frag

glSlang
1
2
3
4
5
6
uniform vec3 uColor;
 
void main()
{
    gl_FragColor = vec4(uColor, 1.0);
}
В "main.py" они читаются и создаётся шейдерная программа следующим образом:

Python
1
2
3
4
5
6
    # Create a shader program
    vertexShaderSource = open("assets/shaders/color.vert").read()
    fragmentShaderSource = open("assets/shaders/color.frag").read()
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
Теперь "main.py" значительно меньше. Создаём массивы для треугольника и квадрата. Квадрат состоит из двух треугольников:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    # Triangle vertex positions
    triangleVertPositions = [
        -0.5, -0.5,
        0.5, -0.5,
        0, 0.5
    ]
 
    # Triangle vertex positions
    squareVertPositions = [
        -0.5, -0.5, # The first triangle for the square
        0.5, -0.5,
        -0.5, 0.5,
        -0.5, 0.5, # The second triangle for the square
        0.5, -0.5,
        0.5, 0.5
    ]
Далее, здесь создаются два объекта - треугольник и квадрат:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    # Create models
    triangle = Model(
        vertPositions = triangleVertPositions,
        program = program,
        position = glm.vec3(50, 50, 0),
        angle = 20,
        scale = glm.vec3(50, 50, 1),
        color = glm.vec3(0.74, 0.58, 0.15))
    square = Model(
        vertPositions = squareVertPositions,
        program = program,
        position = glm.vec3(-50, 50, 0),
        angle = -30,
        scale = glm.vec3(50, 50, 1),
        color = glm.vec3(0.35, 0.78, 0.78))
Вместо массивов выше вы могли бы загрузить в эти объекты массивы из файла dae. Можете попробовать это сделать.

Создаётся общая для всех объектов projView-матрица:

Python
1
2
3
4
5
6
7
    # Create the project and the view matrix
    projMatrix = glm.ortho(-100, 100, -100, 100, 1, -1)
    viewMatrix = glm.lookAt(
        glm.vec3(0, 0, 1),
        glm.vec3(0, 0, 0),
        glm.vec3(0, 1, 0))
    projViewMatrix = projMatrix * viewMatrix
А в функции "main()" вызываются методы рисования объектов с передачей общей projView-матрицы:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    # Main loop
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
        # Clear the canvas
        glClear(GL_COLOR_BUFFER_BIT)
  
        # Draw models
        triangle.draw(projViewMatrix)
        square.draw(projViewMatrix)
 
        # Flip the back the front screen buffer
        pygame.display.flip()
        # Wait 10 msec
        pygame.time.wait(10)
 
if __name__ == "__main__":
    main()


Миниатюры
Вложения
Тип файла: zip model-class.zip (2.9 Кб, 4 просмотров)
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
08.09.2024, 17:00  [ТС]
Здравствуйте! У меня снова появились 10 минут свободного времени. Желаю спросить. Возможно ли скрещивание FBO и класса отрисовки, чтобы это могло работать исправно? Просто желаю все это в выходные, если получиться, объединить.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
08.09.2024, 17:00
Помогаю со студенческими работами здесь

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

Как правильно настроить перемещение камеры?
Добрый день. Недавно купила геймпад китайский, выглядит как xbox 360. Компьютером и всеми играми как 360 определяется, все кнопки работают,...

Перемещение камеры, как в редакторе unity в окне Scene
Всем привет! Подскажите, пожалуйста, как реализовать точно такое же перемещение камеры как в самом unity в окне Scene, когда мы зажимаем...

Перемещение камеры = -перемещение сцены?
Прочитала в redbook что переместить камеру можно 2 способами: переместить саму камеру в прямом направлении или переместить все объекты в...

Как сделать перемещение нескольких файлов с заменой? И как это сделать проще?
Как сделать перемещение нескольких файлов с заменой? Например я ввожу в текстбокс название файлов, потом эти файлы переношу в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
[golang] Алгоритм «Хак Госпера»
alhaos 17.05.2026
Алгоритм «Хак Госпера» Хак Госпера (Gosper's Hack) — алгоритм нахождения следующего по величине числа с тем же количеством установленных бит. Придуман Биллом Госпером в 1970-х, опубликован в. . .
Рисование бинарного древа до 6-го колена на js, svg.
russiannick 17.05.2026
<svg width="335" height="240" viewBox="0 0 335 240" fill="#e5e1bb"> <style> <!]> </ style> <g id="bush"> </ g> </ svg> function fn(){ let rost;/ / высота древа let xx=165,yy=210,w=256;
FSharp: interface of module
DevAlt 16.05.2026
Интерфейс модуля F# позволяет управлять доступностью членов, содержащихся в реализации модуля. По-умолчанию все члены модуля доступны: module Foo let x = 10 let boo () = printfn "boo" . . .
Хитросплетение родственных связей пантеона греческих богов.
russiannick 14.05.2026
Однооконник, позволяющий узреть и изучить отдельных героев древней Греции. <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible". . .
[golang] Угол между стрелками часов
alhaos 12.05.2026
По заданным значениям часа и минуты необходимо определить значение меньшего угла между стрелками аналогового циферблата часов. import "math" func angleClock(hour int, minutes int) float64 { . . .
Debian 13: Установка Lazarus QT5
ВитГо 09.05.2026
Эта инструкция моя компиляция инструкций volvo https:/ / www. cyberforum. ru/ blogs/ 203668/ 10753. html и его же старой инструкции по установке Lazarus с gtk2. . .
Нейросеть на алгоритме "эстафета хвоста" как перспектива.
Hrethgir 06.05.2026
На десерт, когда запущу сервер. Статья тут https:/ / habr. com/ ru/ articles/ 1030914/ . Автор я сам, нейросеть только помогает в вопросах которые мне не известны - не знаю людей которые знали-бы. . .
Асинхронный приём данных из COM-порта
Argus19 01.05.2026
Асинхронный приём данных из COM-порта Купил на aliexpress термопринтер QR701. Он оказался странным. Поключил к Arduino Nano. Был очень удивлён. Наотрез отказывается печатать русские буквы. Чтобы. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru