Форум программистов, компьютерный форум, киберфорум
OpenGL
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.65/104: Рейтинг темы: голосов - 104, средняя оценка - 4.65
155 / 9 / 2
Регистрация: 14.08.2019
Сообщений: 771

Обводка объекта с использованием шейдера

01.10.2021, 19:34. Показов 23362. Ответов 180
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть игра, в которой я могу определить, когда начинает / заканчивает рисоваться нужный мне объект и "внедриться" в нужный момент. Я хочу сделать так, чтобы контур нужного мне объекта обводился. Подобие того что я хочу Вы можете увидеть на прикреплённом изображении.

1. Возможно ли сделать обводку объекта без повторной его отрисовки? (я не знаю как игра рисует объекты, да и это будет сказываться на производительности)
2. Возможно ли сделать это без шейдера и повторной отрисовки? (один человек говорил мне что без шейдера нужно будет рисовать дважды)
3. Как это сделать? (я не прошу написать за меня весь код, однако я очень мало понимаю в шейдерах. У меня есть две "точки" - начало рисования и конец, как и что нужно написать в самом начале чтобы увидеть хоть какие-то изменения и далее уже самому потыкать)
Миниатюры
Обводка объекта с использованием шейдера  
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
01.10.2021, 19:34
Ответы с готовыми решениями:

Обводка объекта
Возможно ли нарисовать обводку объекта, при условиях, что нельзя использовать шейдеры и сам объект рисую не я? У меня есть возможность...

Обводка объекта точками в OpenCV
Здравствуйте,только открыл для себя OpenCV.Задача такова:будут объекты на белом фоне,хочу обвести объект максимальным количеством точек...

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

180
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,109
Записей в блоге: 243
14.10.2021, 15:29
Студворк — интернет-сервис помощи студентам
Если ещё сглаживание (samples = 8) поставить, то лучше смотрится. Для сравнения, как было и как стало:



Миниатюры
0
14.10.2021, 22:24  [ТС]

Не по теме:

8Observer8, Как Вы сделали сглаживание?

0
14.10.2021, 23:16

Не по теме:

Цитата Сообщение от eXpl01TeR Посмотреть сообщение
Как Вы сделали сглаживание?
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    QSurfaceFormat format;
    format.setSamples(8);
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
 
    OpenGLWidget w;
    w.setFormat(format);
    w.show();
 
    return a.exec();
}
За сглаживание отвечает эта строка "format.setSamples(8);" Я использую Qt: https://ru.wikipedia.org/wiki/Qt

0
15.10.2021, 19:04  [ТС]

Не по теме:

8Observer8, Эх, фича Qt
zayats80888, Я тут пытаюсь кое-что понять. Вот тут есть класс Shader. Конструктор класса принимает vertex и fragment шейдеры и компилирует их. Бывает ли так что в одной шейдерной программе есть несколько фрагментных шейдеров или вертексных? Бывает ли так что есть только фрагментный или только вертексный? (Я просто думаю стоит ли переделывать конструктор чтобы он принимал не только фрагментный и вертексный а например мог принимать несколько разных, нужно ли это или нет)

0
155 / 9 / 2
Регистрация: 14.08.2019
Сообщений: 771
15.10.2021, 20:28  [ТС]
zayats80888, Я вот не могу понять, почему хвост лошади увеличен не под его текстуру а под какой-то квадрат. Способ использовал старый, не из этого примера.

Глобальные:

C++
1
2
3
vector<unsigned int> callList;
vector<float> projection;
vector<float> modelview;
Перед отрисовкой моба:

C++
1
2
3
4
5
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
entity = true;
Внутри функции glCallList:

C++
1
2
3
4
5
6
7
8
9
10
11
12
if (entity)
{
    float projectionArray[16]{};
    glGetFloatv(GL_PROJECTION_MATRIX, projectionArray);
 
    float modelviewArray[16]{};
    glGetFloatv(GL_MODELVIEW_MATRIX, modelviewArray);
 
    callList.push_back(list);
    projection.insert(projection.end(), begin(projectionArray), end(projectionArray));
    modelview.insert(modelview.end(), begin(modelviewArray), end(modelviewArray));
}
После отрисовки моба:

C++
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
if (entity)
{
    glStencilMask(0x00);
    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
    glDisable(GL_DEPTH_TEST);
    glUseProgram(shaderProgram);
 
    if (!modelview.empty())
    {
        for (int i = 0; i < modelview.size(); i += 16)
        {
            glMatrixMode(GL_PROJECTION);
            glLoadMatrixf(projection.data() + i);
 
            glMatrixMode(GL_MODELVIEW);
            glLoadMatrixf(modelview.data() + i);
 
            glScalef(1.1f, 1.1f, 1.1f);
            pglCallList(callList[i / 16]);
        }
 
        projection.clear();
        modelview.clear();
        callList.clear();
    }
 
    glEnable(GL_DEPTH_TEST);
    glUseProgram(0);
 
    glDisable(GL_STENCIL_TEST);
    glStencilMask(0xFF);
 
    entity = false;
}
Такое ощущение, будто оно обводит и "прозрачные" пиксели которых "не видно". Может, можно как-то сделать, чтобы обводило только те которые видны?
Миниатюры
Обводка объекта с использованием шейдера   Обводка объекта с использованием шейдера  
0
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,109
Записей в блоге: 243
16.10.2021, 12:20
Цитата Сообщение от eXpl01TeR Посмотреть сообщение
Бывает ли так что в одной шейдерной программе есть несколько фрагментных шейдеров или вертексных? Бывает ли так что есть только фрагментный или только вертексный?
К одной шейдерной программе не может быть прикреплено больше одного вершинного и больше одного фрагментного шейдера. Может быть ещё один геометрический (или вычислительный и т.д.). Как минимум должен быть один вершинный и один фрагментный. Они в паре работают. Вершинный устанавливает вершины треугольников, считывая их из буфера (из VBO - Vertex Buffer Object). Когда вершинный шейдер, с учётом переданных в шейдер матриц трансформаций, выставил координаты вершин треугольников, то в дело вступает фрагментный шейдер. Фрагментный шейдер вызывается для каждого фрагмента треугольника и присваивает фрагменту цвет, а фрагмент может содержать несколько пикселей. Фрагментный шейдер ещё называют "пиксельный". Проще думать, что фрагментный шейдер срабатывает для каждого пикселя отдельно и устанавливает каждому пикселю свой цвет. Нужно отметить, что шейдеры могут быть запущены одновременно тысячами параллельно. Простое объяснение теории работы шейдеров можно почитать в этой книге: WebGL. Программирование трехмерной графики

Слово "вертексный" - это калька с английского "vertex", что переводится, как "вершина". На русском правильно будет "треугольник состоит из трёх вершин", а не "треугольник состоит из трёх вертексов". В отношении шейдера "вертексный" или "вершинный" - и так и так употребляют, это не является грубой ошибкой и может даже совсем ошибкой не является, но, как мне кажется, более логично писать "вершинный".
0
Модератор
Эксперт Java
 Аватар для alecss131
2896 / 1402 / 412
Регистрация: 11.08.2017
Сообщений: 4,489
Записей в блоге: 2
16.10.2021, 12:33
Цитата Сообщение от 8Observer8 Посмотреть сообщение
(или вычислительный и т.д.)
Вычислительный шейдер к графическому пайплайну отношения не имеет, он может спокойно жить сам по себе без вершинного и фрагментного шейдеров. Вычислительный это аналог opencl/cuda но для Opengl. Шейдерная программа из одного вычислительного шейдера корректная программа.
А помимо геометрического шейдера могут быть еще пару шейдеров, причем именно пара, это пара шейдеров тесселяции (по одному не работают), а точнее управления тесселяцией и выполнения тесселяции.
Но правда сейчас пара вершинный/фрагментный щейдер не всегда актуальна, на смену им могут придти новая пара меш/фрагментный, где меш шейдер это заменя целой пачки старых шейдеров (вершинный + геометрический + пара тесселяции). И новый необязательный этап конвеера это таск шейдер, идет до меш шейдера. Это правда доступно только на нвидиа тюринг и новее картах.

Я не говорю про новые возможности более новых версий opengl. Где шейдерная программа может состоять из 1 шейдера, но при этом шейдер как таковой использовать нельзя, надо как минимум пару (вершинный/вычислительный) назначить пайплайну который и привязывается для отрисовки. Это имхо удобнее, так как не надо плодить пары шейдеров, а можно на ходу перепривязывать. И это еще ближе по концепции современного и актуального апи под названием vulkan.
Сейчас многие кто считают Opengl устаревшей и вообще легаси технологиями, тем более что apple вообще хотят удалить поддержку у себя.
1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,109
Записей в блоге: 243
16.10.2021, 13:20
Спасибо, я просто не изучал других шейдеров. У меня видеокарты на ноутбуке и смартфоне не поддерживают Vulkan. Я кроме вершинного и фрагментного шейдеров других шейдеров не изучал и пока точно не собираюсь, потому что времени не будет хватать на более приоритетные для меня задачи.

Мои целевые платформы - это:
  • веб (WebGL)
  • смартфоны/планшеты (Android/OpenGLES)
  • настольные ПК и ноутбуки (Windows и Linux)
0
 Аватар для zayats80888
6353 / 3524 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
16.10.2021, 13:59
Цитата Сообщение от 8Observer8 Посмотреть сообщение
не может быть прикреплено больше одного вершинного и больше одного фрагментного шейдера
Формально это не так. Вам, как человеку, знакомому с языком C(С++), возможно, будет интересно знать, что вы можете прикрепить к программе несколько шейдеров. Как и в языке C, процесс сборки программы состоит из двух стадий - компиляции и линковки. Так вот объект шейдера - это аналог единицы трансляции в языке С, каждый шейдер компилируется в объектный файл, а объект программы - это собственно исполняемый файл, который линкуется из объектных файлов. Тут важно, чтобы у каждой стадии(фрагментный, вершинный и т.д.) была только одна точка входа(функция main).
Например, у вас фрагментная стадия может собирать из разных шейдеров, в зависимости от опций приложения:
glSlang
1
2
3
4
5
6
7
8
9
10
11
// основной шейдер "main" с точкой входа
 
//...
 
float some_function(float param); // объявление функции, которая там что-то считает
 
void main() {
//...
float some_param = some_function(in_param);
//...
}
glSlang
1
2
3
4
5
6
7
// шейдер "high_quality" с реализацией some_function
 
uniform float additional_param; // возможно, дополнительный параметр
 
float some_function(float param) {
  //...
}
glSlang
1
2
3
4
5
// шейдер "low_quality" с реализацией some_function
 
float some_function(float param) {
  //...
}
Вы можете хранить коллекцию откомпилированных шейдеров и, в зависимости от ситуации, компоновать программу из нужной комбинации, например, вы присоединяете(attach) фрагментные шейдеры "main" и "low_quality" и линкуете(link) программу, а позже можете просто отсоединить(detach) шейдер "low_quality", присоединить шейдер "high_quality" и перелинковать(link) программу. Таким образом можно удобно комбинировать каждую стадию из множества шейдеров, а не писать лапшу кода на препроцессоре(#ifdef ...) с редактированием исходного кода в рантайме.
1
1977 / 833 / 115
Регистрация: 01.10.2012
Сообщений: 5,152
Записей в блоге: 2
17.10.2021, 10:17
Цитата Сообщение от zayats80888 Посмотреть сообщение
Таким образом можно удобно комбинировать каждую стадию из множества шейдеров, а не писать лапшу кода на препроцессоре(#ifdef ...) с редактированием исходного кода в рантайме.
Вероятно (или наверняка) "low_quality" и "high_quality" имеют хорошую общую часть. Вносить изменения в серию/множество шейдеров быстро станет невыносимым. Поэтому да, лапша #fdef + редактирование в runtime - это ужасно, но лучшего нет.

Добавлено через 15 минут
Цитата Сообщение от 8Observer8 Посмотреть сообщение
Фрагментный шейдер вызывается для каждого фрагмента треугольника и присваивает фрагменту цвет, а фрагмент может содержать несколько пикселей. Фрагментный шейдер ещё называют "пиксельный". Проще думать, что фрагментный шейдер срабатывает для каждого пикселя отдельно и устанавливает каждому пикселю свой цвет.
Наоборот, один пиксель может содержать неск фрагментов 1 pixel = 1 fragment - это грубый рендер без всякого анти-алиаса ("зубы"). Название "фрагментный" грамотное, а вот "пиксельный" - нет. Шейдер должен шейдить то что ему дали, это совсем необязательно (суб)пиксель
1
1472 / 827 / 140
Регистрация: 12.10.2013
Сообщений: 5,456
17.10.2021, 11:53
За 2 недели уже можно было написать свой майнкрафт чисто побегать офлайн с полным контролем всего.

Не проще вместо обводки менять цвет через наложение фильтра?
Есть фильтры выделения границ Собеля можно сделать забавный эффект.
https://ru.wikipedia.org/wiki/Оператор_Собеля
1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,109
Записей в блоге: 243
17.10.2021, 23:50
Цитата Сообщение от eXpl01TeR Посмотреть сообщение
Не по теме:
, Твои жалкие попытки меня спровоцировать ни к чему не приведут. Единственное, чему я удивлен - отметке спасибо от модератора, который по логике как раз таки должен был отправить твою простыню в мусорную корзину (предварительно вытерев об неё ноги) и выдать тебе наказание) Видимо обиделся на видео которое я отправлял ранее, но что поделать, я действительно до сих пор считаю что ограничение размера изображений - глупость)
Отправляйте таких в список игнорирования (находится в профиле). А лучше ещё поставьте платин для браузера (могу показать, как это сделать), чтобы автоматически удалялись HTML-элементы по никам. Например, я даже не знаю, кому вы отвечали и кого вы цитировали. Не стоит тратить время на чтение комментариев определённых личностей, лучше убирать их в список игнорирования.



Миниатюра
1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,109
Записей в блоге: 243
18.10.2021, 00:10
eXpl01TeR, я могу вам показать, как работают шейдеры на примере Qt. Для начала, мы создали пустое окно, где функция glClearColor(0.2f, 0.5f, 0.1f, 1.f) выставляет цвет фона тёмно-зелёным, а glClear(GL_COLOR_BUFFER_BIT) заливает фон заданным цветом. Метод initializeGL() вызывается один раз и в нём находится вызов функции glClearColor, чтобы выставить цвет один раз. Метод paintGL вызывается автоматически каждый раз, когда программе нужно перерисовать клиентскую область окна. Замечу, что ничего подключать не нужно, в Qt всё базовое уже есть. В файл .pro нужно добавить строку "win32: LIBS += -lopengl32", то так:

Code
1
2
3
QT       += core gui
 
win32: LIBS += -lopengl32
Я работаю на ноутбуке, где две видео карты, поэтому я принудительно запускаю с более мощной, учитывая, что программа может быть запущена на ноутбуке с Geforce и Radeon, поэтому я добавляю эти строки в main.cpp:

C++ (Qt)
1
2
3
4
5
#ifdef _WIN32
#include <windows.h>
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
#endif


C++ (Qt)
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
#ifdef _WIN32
#include <windows.h>
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
#endif
 
#include <QtWidgets/QApplication>
#include <QtWidgets/QOpenGLWidget>
 
 
class Widget : public QOpenGLWidget
{
public:
    Widget()
    {
        setWindowTitle("OpenGL and Qt");
        resize(400, 400);
    }
 
    void initializeGL() override
    {
        glClearColor(0.2f, 0.5f, 0.1f, 1.f);
    }
 
    void paintGL() override
    {
        glClear(GL_COLOR_BUFFER_BIT);
    }
 
    void resizeGL(int w, int h) override
    {
        glViewport(0, 0, w, h);
    }
};
 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}
1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,109
Записей в блоге: 243
18.10.2021, 00:21
Для создания шейдерной программы нужно подключить заголовок:

C++ (Qt)
1
#include <QtGui/QOpenGLShaderProgram>
Создаём шейдерную программу в классе в нашем классе Widget:

C++ (Qt)
1
2
private:
    QOpenGLShaderProgram _program;
В initializeGL напишем исходные коды вершинного и фрагментного шейдеров, а так же напишем код для добавления шейдеров в шейдерную программу (при добавлении происходит компиляция шейдеров) и добавим команду линковки шейдеров. Если запустить проект, то увидим тоже самое окно, что и выше, но шейдерная программа была создана:

C++ (Qt)
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
#ifdef _WIN32
#include <windows.h>
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
#endif
 
#include <QtWidgets/QApplication>
#include <QtWidgets/QOpenGLWidget>
#include <QtGui/QOpenGLShaderProgram>
 
 
class Widget : public QOpenGLWidget
{
public:
    Widget()
    {
        setWindowTitle("OpenGL and Qt");
        resize(400, 400);
    }
 
private:
    QOpenGLShaderProgram _program;
 
private:
    void initializeGL() override
    {
        glClearColor(0.2f, 0.5f, 0.1f, 1.f);
 
        const char *vertShaderSrc =
                "#version 330 core\n"
                "in vec3 aPosition;"
                "void main()"
                "{"
                "    gl_Position = vec4(aPosition, 1.0);"
                "}";
        const char *fragShaderSrc =
                "#version 330 core\n"
                "out vec4 fragColor;"
                "void main()"
                "{"
                "    fragColor = vec4(0.0, 0.0, 0.5, 1.0);"
                "}";
 
        _program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertShaderSrc);
        _program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShaderSrc);
        _program.link();
    }
 
    void paintGL() override
    {
        glClear(GL_COLOR_BUFFER_BIT);
    }
 
    void resizeGL(int w, int h) override
    {
        glViewport(0, 0, w, h);
    }
};
 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}
1
1472 / 827 / 140
Регистрация: 12.10.2013
Сообщений: 5,456
18.10.2021, 13:34
_Develop, Вот у вас есть блокировка не только по списку игнора форума но и в браузере десятка людей? Подозреваю он может быть просто социально опасен. Может уже был изолирован вы же не знаете что там за человек. Ворвется на пары в любой универ начнет там рубать всех кто юзает deprecated glut...боже это десятки вузов. А те его примеры слизанные с книги…это ж авторские права, я бы побоялся вылаживать их так. Могут вообще закрыть парю + штраф форуму.

Добавлено через 7 минут
eXpl01TeR, Если такие сложности выделить контур объекта можно выделить чисто рамкой как во всех стратегиях или вообще жирной точкой как в читах wallhack.
1
9987 / 2959 / 497
Регистрация: 05.10.2013
Сообщений: 8,109
Записей в блоге: 243
18.10.2021, 13:55
Заметьте, что для использования функций OpenGL (glClearColor и glClear) я не скачивал и не подключал никаких библиотек, это есть в Qt. Просто покажу элементарную работу шейдеров по рисованию треугольника в 3D пространстве. Если вы знаете, как нарисовать один треугольник, то с помощью множества треугольников можете нарисовать всё что угодно. Воспользуемся матрицами модели, перспективы, вида и модели. Для создания матриц в Qt не нужно скачивать и подключать GLM. Матрицы уже есть в Qt. Подключаем матрицы:

C++ (Qt)
1
#include <QtGui/QMatrix4x4>
Создаём матрицы:

C++ (Qt)
1
2
3
4
    QMatrix4x4 _projMatrix;  // Матрица перспективной проекции
    QMatrix4x4 _viewMatrix;  // Матрица вида
    QMatrix4x4 _modelMatrix; // Матрица модели
    QMatrix4x4 _mvpMatrix;   // Итоговая матрица для передачи в вершинный шейдер
Матрица модели задаёт размер, поворот и позицию 3D-модели. Матрица вида задаёт положение камеры. Перспективная матрица создаёт ощущение перспективы. Матрицу вида и матрицу модели создадим в методе initializeGL():

C++ (Qt)
1
2
3
4
5
6
7
8
        _modelMatrix.translate(0.f, 0.f, 0.f); // Задаём позицию треугольника
        _modelMatrix.rotate(20.f, QVector3D(0.f, 1.f, 0.f)); // Поворачиваем треугольник вокруг вертикальной оси
        _modelMatrix.scale(5.f, 5.f, 5.f); // Задаём размер треугольнику (увеличиваем в 5 раз)
        
        _viewMatrix.lookAt(
                    QVector3D(0.f, 0.f, 10.f),  // Позиция камеры
                    QVector3D(0.f, 0.f, 0.f),  // В какую точку смотри камера
                    QVector3D(0.f, 1.f, 0.f)); // Ориентация камеры, его up-вектор направлен вверх
А матрицу перспективы создадим в методе resizeGL(int w, int h). Когда соотношение сторон окна изменилось, то нужно пересчитывать матрицу перспективы, но предварительно её нужно сбросить в единичную:

C++ (Qt)
1
2
3
4
5
6
    void resizeGL(int w, int h) override
    {
        _projMatrix.setToIdentity();
        _projMatrix.perspective(50.f, w / (float)h, 0.1f, 1000.f);
        glViewport(0, 0, w, h);
    }
Теперь нужно задать вершины треугольника в массиве float. Сохранить этот массив в оперативной памяти видео карты. Чтобы сохранить, нужно создать объект VertexBufferObject, привязаться к этому объекту (с помощью вызова метода bind()), и отправить ему массив вершин треугольника. Перед этим посмотрим на вершинный шейдер:

C++ (Qt)
1
2
3
4
5
6
7
8
        const char *vertShaderSrc =
                "#version 330 core\n"
                "in vec3 aPosition;"
                "uniform mat4 uMvpMatrix;"
                "void main()"
                "{"
                "    gl_Position = uMvpMatrix * vec4(aPosition, 1.0);"
                "}";
После вызовам метода рисования glDrawArrays (или glDrawElements) будет запущен вершинный шейдер и отработает он три раза и каждый раз в aPosition будет попадать вершина треугольника. Создадим массив вершин треугольника и настроим (тип данных GL_FLOAT, offset = 0, последний параметр равен 3 - это сколько значений на вершину).

Подключим буфер:

C++ (Qt)
1
#include <QtGui/QOpenGLBuffer>
Создадим буфер:

C++ (Qt)
1
QOpenGLBuffer _vertPosBuffer;
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
        float vertPositions[] = {
            -0.5f, -0.5f, 0.f,
            0.5f, -0.5f, 0.f,
            0.f, 0.5f, 0.f
        };
        _vertPosBuffer.create(); // Создаём буфер
        _vertPosBuffer.bind();   // Привязываемся к буферу
        _vertPosBuffer.allocate(vertPositions, sizeof(vertPositions)); // Передаём вершины в буфер
        _program.bind(); // Привязываемся к шейдерной программе
        int aPositionLocation = _program.attributeLocation("aPosition"); // Берём идентификатор переменной из шейдера
        _program.setAttributeBuffer(aPositionLocation, GL_FLOAT, 0, 3); // Делаем настройку
        _program.enableAttributeArray(aPositionLocation); // Активируем настройку
В методе initializeGL нужно получить идентификатор переменной uMvpMatrix. В эту переменную будет загружать результат умножения матриц. Сначала на вершину действуем матрица модели (сначала матрица размера, потом матрица поворота и потом матрица перемещения), потом матрица вида, а в конце - матрица перспективы. Умножать нужно в том порядке, в каком действую матрицы и передаём результирующую матрицу в вершинный шейдер:

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    void initializeGL()
    {
        /* ... */
 
        _uMvpMatrixLocation = _program.uniformLocation("uMvpMatrix");
    }
 
    void paintGL() override
    {
        glClear(GL_COLOR_BUFFER_BIT);
        _mvpMatrix = _projMatrix * _viewMatrix * _modelMatrix;
        _program.bind(); // Привязываемся к шейдерной программе
        _program.setUniformValue(_uMvpMatrixLocation, _mvpMatrix);
        glDrawArrays(GL_TRIANGLES, 0, 3);
    }
Фрагментный шейдер будет вызван для каждого пикселя треугольника и выставит каждому пикселю треугольника синий цвет:

C++ (Qt)
1
2
3
4
5
6
7
        const char *fragShaderSrc =
                "#version 330 core\n"
                "out vec4 fragColor;"
                "void main()"
                "{"
                "    fragColor = vec4(0.0, 0.0, 0.5, 1.0);"
                "}";
Получается синий треугольник повёрнутый на 20 градусов вокруг вертикальной оси Y:



main.cpp

C++ (Qt)
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
#ifdef _WIN32
#include <windows.h>
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
#endif
 
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLBuffer>
#include <QtGui/QMatrix4x4>
#include <QtWidgets/QApplication>
#include <QtWidgets/QOpenGLWidget>
 
 
class Widget : public QOpenGLWidget
{
public:
    Widget()
    {
        setWindowTitle("OpenGL and Qt");
        resize(400, 400);
    }
 
private:
    QOpenGLShaderProgram _program;
    QMatrix4x4 _projMatrix;  // Матрица перспективной проекции
    QMatrix4x4 _viewMatrix;  // Матрица вида
    QMatrix4x4 _modelMatrix; // Матрица модели
    QMatrix4x4 _mvpMatrix;   // Итоговая матрица для передачи в вершинный шейдер
    QOpenGLBuffer _vertPosBuffer;
    int _uMvpMatrixLocation; // Идентификатор результирующей матрицы из вершинного шейдера
 
private:
    void initializeGL() override
    {
        glClearColor(0.2f, 0.5f, 0.1f, 1.f);
 
        const char *vertShaderSrc =
                "#version 330 core\n"
                "in vec3 aPosition;"
                "uniform mat4 uMvpMatrix;"
                "void main()"
                "{"
                "    gl_Position = uMvpMatrix * vec4(aPosition, 1.0);"
                "}";
        const char *fragShaderSrc =
                "#version 330 core\n"
                "out vec4 fragColor;"
                "void main()"
                "{"
                "    fragColor = vec4(0.0, 0.0, 0.5, 1.0);"
                "}";
 
        _program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertShaderSrc);
        _program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShaderSrc);
        _program.link();
 
        _modelMatrix.translate(0.f, 0.f, 0.f); // Задаём позицию треугольника
        _modelMatrix.rotate(20.f, QVector3D(0.f, 1.f, 0.f)); // Поворачиваем треугольник вокруг вертикальной оси
        _modelMatrix.scale(5.f, 5.f, 5.f); // Задаём размер треугольнику (увеличиваем в 5 раз)
 
        _viewMatrix.lookAt(
                    QVector3D(0.f, 0.f, 10.f),  // Позиция камеры
                    QVector3D(0.f, 0.f, 0.f),  // В какую точку смотри камера
                    QVector3D(0.f, 1.f, 0.f)); // Ориентация камеры, его up-вектор направлен вверх
 
        float vertPositions[] = {
            -0.5f, -0.5f, 0.f,
            0.5f, -0.5f, 0.f,
            0.f, 0.5f, 0.f
        };
        _vertPosBuffer.create(); // Создаём буфер
        _vertPosBuffer.bind();   // Привязываемся к буферу
        _vertPosBuffer.allocate(vertPositions, sizeof(vertPositions)); // Передаём вершины в буфер
        _program.bind(); // Привязываемся к шейдерной программе
        int aPositionLocation = _program.attributeLocation("aPosition"); // Берём идентификатор переменной из шейдера
        _program.setAttributeBuffer(aPositionLocation, GL_FLOAT, 0, 3); // Делаем настройку
        _program.enableAttributeArray(aPositionLocation); // Активируем настройку
 
        _uMvpMatrixLocation = _program.uniformLocation("uMvpMatrix");
    }
 
    void paintGL() override
    {
        glClear(GL_COLOR_BUFFER_BIT);
        _mvpMatrix = _projMatrix * _viewMatrix * _modelMatrix;
        _program.bind(); // Привязываемся к шейдерной программе
        _program.setUniformValue(_uMvpMatrixLocation, _mvpMatrix);
        glDrawArrays(GL_TRIANGLES, 0, 3);
    }
 
    void resizeGL(int w, int h) override
    {
        _projMatrix.setToIdentity();
        _projMatrix.perspective(50.f, w / (float)h, 0.1f, 1000.f);
        glViewport(0, 0, w, h);
    }
};
 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}
Миниатуры
1
155 / 9 / 2
Регистрация: 14.08.2019
Сообщений: 771
18.10.2021, 14:41  [ТС]
8Observer8, Вы так много и подробно всего расписали, я впервые в жизни пожалел что у меня нет Qt Но принцип я вроде-как понял, спасибо <3
1
155 / 9 / 2
Регистрация: 14.08.2019
Сообщений: 771
19.10.2021, 22:29  [ТС]
zayats80888, Я таки додумался как можно было бы нае обмануть систему и у меня получилось более-менее правильно отрисовать увеличенную модельку. Тот же способ со Stencil буфером что мы обсуждали в самом начале, только теперь я стал генерировать свой callList до отрисовки и вызывать его после.

Однако данный способ как по мне ужасный по двум трём причинам:
1. Генерация и удаление list'а на каждого моба КАЖДЫЙ КАДР.
2. Выглядит ужасно. На скриншотах из чита выше используется явно какой-то другой способ.
3. Не на всех модельках корректно работает
Миниатюры
Обводка объекта с использованием шейдера  
0
 Аватар для zayats80888
6353 / 3524 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
19.10.2021, 22:32
eXpl01TeR, ну так а что ты способ с фреймбуфером не попробовал?
0
155 / 9 / 2
Регистрация: 14.08.2019
Сообщений: 771
19.10.2021, 22:35  [ТС]
zayats80888, Он мне показался ещё сложнее чем этот, а я этот очень слабо понимаю.

Добавлено через 52 секунды
zayats80888, Но вообще спасибо большое, если бы не ты я бы вообще никакого прогресса не имел. А тут вон уже что умею.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
19.10.2021, 22:35

Создание текста с использованием DrawString и обводка
Подскажите, пожалуйста, как создать такой текст с обводкой, например буква, как буква «T» на картинке ниже. Для создания текста использую...

Создать проект, с использованием объекта Timer, перемещение графического объекта по заданной траектории, (прямоугольник,
Создать проект, с использованием объекта Timer, перемещение графического объекта по заданной траектории, (прямоугольник, квадрат или...

Перемещение объекта по форме с использованием мыши
Всем доброго времени суток. Необходимо осуществить перемещение Image при помощи мыши. Объекты находятся в Canvas. Проблема заключена в...

Задача с использованием объекта hScrollBar и pictureBox
Может мой вопрос покажется глупым, но всё же...Как можно осуществить изменение размера объекта pictureBox используя hScrollBar? У меня была...

Поворот объекта с использованием Rigidbody.MoveRotation
использую Rigidbody.MoveRotation для поворота объекта по оси Х на 30 градусов, проблема в том что оно НЕ затухает при 30 градусов, а просто...


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

Или воспользуйтесь поиском по форуму:
120
Ответ Создать тему
Новые блоги и статьи
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru