alexm_notabenoid: переводы
OpenGL SuperBible (6th Edition) / OpenGL Супер Книга (6-ое издание)
→Click here to view code image
void glBufferSubData(GLenum target,
GLintptr offset,
GLsizeiptr size,
const GLvoid * data);
void glBufferSubData(GLenum target,
GLintptr offset,
GLsizeiptr size,
const GLvoid * data);
0 void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);
→After the code in Listing 5.1 has executed, buffer contains the name of a buffer object that has been
initialized to represent one megabyte of storage for whatever data we choose. Using the GL_ARRAY_BUFFER
target to refer to the buffer object suggests to OpenGL that we’re planning to use this buffer to store
vertex data, but we’ll still be able to take that buffer and bind it to some other target later. There are a
handful of ways to get data into the buffer object. You may have noticed the NULL pointer that we pass
as the third argument to glBufferData() in Listing 5.1. Had we instead supplied a pointer to some
data, that data would have been used to initialize the buffer object. Another way to get data into a
buffer is to give it to OpenGL and tell it to copy data there. To do this, we call glBufferSubData(),
passing the size of the data we want to put into the buffer, the offset in the buffer where we want it to
go, and a pointer to the data in memory that should be put into the buffer. glBufferSubData() is
declared as
initialized to represent one megabyte of storage for whatever data we choose. Using the GL_ARRAY_BUFFER
target to refer to the buffer object suggests to OpenGL that we’re planning to use this buffer to store
vertex data, but we’ll still be able to take that buffer and bind it to some other target later. There are a
handful of ways to get data into the buffer object. You may have noticed the NULL pointer that we pass
as the third argument to glBufferData() in Listing 5.1. Had we instead supplied a pointer to some
data, that data would have been used to initialize the buffer object. Another way to get data into a
buffer is to give it to OpenGL and tell it to copy data there. To do this, we call glBufferSubData(),
passing the size of the data we want to put into the buffer, the offset in the buffer where we want it to
go, and a pointer to the data in memory that should be put into the buffer. glBufferSubData() is
declared as
0 После исполнения кода в Листинге 5.1, буфер содержит имя объекта буфера который был инициализирован для хранения 1 Мб данных для данных которые мы выберем. Использование GL_ARRAY_BUFFER подсказывает OpenGL что мы собираем использовать этот буфер для хранения вершинных данных, но мы сможем взять этот буфер и использовать для других целей. Есть несколько способов передачи данных в буфер. Вы возможно заметили что мы передали в качестве указателя NULL третьим аргументом функции glBufferData() в Листинге 5.1. Если бы мы передали данные в качестве указателя, то они бы использовались для инициализации буфера. Другой путь передачи данных в буфер это дать их OpenGL и сказать чтобы он их скопировал их в буфер. Чтобы сделать это мы вызываем glBufferSubData(), передавая size данных мы хотим их переместить в буфер, offset это смещение в буфере куда мы хотим их переместить и указатель на данные в памяти которые переместятся в буфер.
→Click here to view code image
// The type used for names in OpenGL is GLuint
GLuint buffer;
// Generate a name for the buffer
glGenBuffers(1, &buffer);
// Now bind it to the context using the GL_ARRAY_BUFFER binding point
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Specify the amount of storage we want to use for the buffer
glBufferData(GL_ARRAY_BUFFER, 1024 * 1024, NULL, GL_STATIC_DRAW);
Listing 5.1: Generating, binding, and initializing a buffer
// The type used for names in OpenGL is GLuint
GLuint buffer;
// Generate a name for the buffer
glGenBuffers(1, &buffer);
// Now bind it to the context using the GL_ARRAY_BUFFER binding point
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Specify the amount of storage we want to use for the buffer
glBufferData(GL_ARRAY_BUFFER, 1024 * 1024, NULL, GL_STATIC_DRAW);
Listing 5.1: Generating, binding, and initializing a buffer
0 GLuint buffer;
//Создаём имя для буфера
glGenBuffers( 1, &buffer );
//Теперь присоединяем к контексту используя GL_ARRAY_BUFFER
glBingBuffer( GL_ARRAY_BUFFER, buffer );
//Укажем объём памяти который мы хотим использовать для буфера
glBufferData(GL_ARRAY_BUFFER, 1024 * 1024, NULL, GL_STATIC_DRAW);
//Создаём имя для буфера
glGenBuffers( 1, &buffer );
//Теперь присоединяем к контексту используя GL_ARRAY_BUFFER
glBingBuffer( GL_ARRAY_BUFFER, buffer );
//Укажем объём памяти который мы хотим использовать для буфера
glBufferData(GL_ARRAY_BUFFER, 1024 * 1024, NULL, GL_STATIC_DRAW);
→Table 5.1. Buffer Object Usage Models
Listing 5.1 shows how a name for a buffer is reserved by calling glGenBuffers(), how it is
bound to the context using glBindBuffer(), and how storage for it is allocated by calling
glBufferData().
Listing 5.1 shows how a name for a buffer is reserved by calling glGenBuffers(), how it is
bound to the context using glBindBuffer(), and how storage for it is allocated by calling
glBufferData().
0 Таблица 5.1 Использование буфера
Листинг 5.1 показывает как резервировать имя с помощью glGenBuffers(), как присоединять к контексту используя glBingBuffer() и как выделяется под него вызовом glBufferData().
Листинг 5.1 показывает как резервировать имя с помощью glGenBuffers(), как присоединять к контексту используя glBingBuffer() и как выделяется под него вызовом glBufferData().
→The target parameter tells OpenGL which target the buffer you want to allocate storage for is bound
to. For example, the binding point that is used when you want to use a buffer to store data that
OpenGL can put into your vertex attributes is called the GL_ARRAY_BUFFER binding point. Although you
may hear the term vertex buffer or uniform buffer, unlike some graphics libraries, OpenGL doesn’t
really assign types to buffers — a buffer is just a buffer and can be used for any purpose at any time
(and even multiple purposes at the same time, if you like). The size parameter tells OpenGL how big
the buffer should be, and data is a pointer to some initial data for the buffer (it can be NULL if you don’t
have data to put in the buffer right away). Finally, usage tells OpenGL how you plan to use the buffer.
There are a number of possible values for usage, which are listed in Table 5.1.
to. For example, the binding point that is used when you want to use a buffer to store data that
OpenGL can put into your vertex attributes is called the GL_ARRAY_BUFFER binding point. Although you
may hear the term vertex buffer or uniform buffer, unlike some graphics libraries, OpenGL doesn’t
really assign types to buffers — a buffer is just a buffer and can be used for any purpose at any time
(and even multiple purposes at the same time, if you like). The size parameter tells OpenGL how big
the buffer should be, and data is a pointer to some initial data for the buffer (it can be NULL if you don’t
have data to put in the buffer right away). Finally, usage tells OpenGL how you plan to use the buffer.
There are a number of possible values for usage, which are listed in Table 5.1.
0 Параметр target говорит OpenGL под какой буфер вы хотите выделить память. Например, буфер который будет использоваться для хранения вершинных атрибутов называется GL_ARRAY_BUFFER. Возможно вы слышали термин вершинный буфер или юниформ буфер OpenGL, в отличии некоторых графических библиотек, на самом деле не назначает типы буферам -- буфер просто буфер и его можно использовать когда угодно и в любых целя ( и даже для нескольких задач если хотите ). Параметр size указывает OpenGL какого размера должен быть буфер и data является указателем на данные для буфера ( он может быть NULL если вы не хотите заполнять буфер сразу ). Наконец, usage говорит OpenGL как вы хотите использовать буфер. В Таблице 5.1 перечисленные возможные значения использования.
→Click here to view code image
void glBufferData(GLenum target,
GLsizeiptr size,
const GLvoid * data,
GLenum usage);
void glBufferData(GLenum target,
GLsizeiptr size,
const GLvoid * data,
GLenum usage);
0 void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);
→Allocating Memory using Buffers
The function that is used to allocate memory using a buffer object is glBufferData(), whose
prototype is
The function that is used to allocate memory using a buffer object is glBufferData(), whose
prototype is
0 Выделение памяти используя буферы
Функция которая используется для выделения памяти под буфер это glBufferData() прототип поторой
Функция которая используется для выделения памяти под буфер это glBufferData() прототип поторой
→Buffers
In OpenGL, buffers are linear allocations of memory that can be used for a number of purposes.
They are represented by names, which are essentially opaque handles that OpenGL uses to identify
them. Before you can start using buffers, you have to ask OpenGL to reserve some names for you and
then use them to allocate memory and put data into that memory. The memory allocated for a buffer
object is called its data store. Once you have the name of a buffer, you can attach it to the OpenGL
context by binding it to a buffer binding point. Binding points are sometimes referred to as targets,1
and the terms may be used interchangeably. There are a large number of buffer binding points in
OpenGL, and each has a different use. For example, you can use the contents of a buffer to
automatically supply the inputs of a vertex shader, to store the values of variables that will be used by
your shaders, or as a place for shaders to store the data they produce.
1. It’s not technically correct to conflate target and binding point as a single target may have multiple binding points. However,
for most use cases, it is well understood what is meant.
In OpenGL, buffers are linear allocations of memory that can be used for a number of purposes.
They are represented by names, which are essentially opaque handles that OpenGL uses to identify
them. Before you can start using buffers, you have to ask OpenGL to reserve some names for you and
then use them to allocate memory and put data into that memory. The memory allocated for a buffer
object is called its data store. Once you have the name of a buffer, you can attach it to the OpenGL
context by binding it to a buffer binding point. Binding points are sometimes referred to as targets,1
and the terms may be used interchangeably. There are a large number of buffer binding points in
OpenGL, and each has a different use. For example, you can use the contents of a buffer to
automatically supply the inputs of a vertex shader, to store the values of variables that will be used by
your shaders, or as a place for shaders to store the data they produce.
1. It’s not technically correct to conflate target and binding point as a single target may have multiple binding points. However,
for most use cases, it is well understood what is meant.
0 Буферы
В OpenGL буферы являются линейными данными ( linear allocations of memory ) которые можно использовать для различных целей. Они представлены именами которые являются непрозрачными ссылками ( opaque handles прим. пер. ) которые OpenGL использует для их идентификации. Прежде чем начать использовать буфер, вы должны попросить OpenGL зарезервировать имя для вас, затем использовать его для выделения памяти и для заполнения данными. Память выделяемая для буфера называется хранилищем данных.
В OpenGL буферы являются линейными данными ( linear allocations of memory ) которые можно использовать для различных целей. Они представлены именами которые являются непрозрачными ссылками ( opaque handles прим. пер. ) которые OpenGL использует для их идентификации. Прежде чем начать использовать буфер, вы должны попросить OpenGL зарезервировать имя для вас, затем использовать его для выделения памяти и для заполнения данными. Память выделяемая для буфера называется хранилищем данных.
→In the examples you’ve seen so far, we have either used hard-coded data directly in our shaders, or we have passed values to shaders one at a time. While sufficient to demonstrate the configuration of the OpenGL pipeline, this is hardly representative of modern graphics programming. Recent graphics processors are designed as streaming processors that consume and produce huge amounts of data.
Passing a few values to OpenGL at a time is extremely inefficient. To allow data to be stored and accessed by OpenGL, we include two main forms of data storage — buffers and textures. In this chapter, we first introduce buffers, which are linear blocks of un-typed data and can be seen as generic memory allocations. Next, we introduce textures, which are normally used to store multidimensional data, such as images or other data types.
Passing a few values to OpenGL at a time is extremely inefficient. To allow data to be stored and accessed by OpenGL, we include two main forms of data storage — buffers and textures. In this chapter, we first introduce buffers, which are linear blocks of un-typed data and can be seen as generic memory allocations. Next, we introduce textures, which are normally used to store multidimensional data, such as images or other data types.
0 В примерах, которые мы видели до сих пор, мы либо жёстко программировали данные в шейдерах либо мы передавали все значения за раз. Хотя этого достаточно для демонстрации работы конвейера OpenGL, вряд ли это показывает современное программирование графики. Современные графические процессоры спроектированы как потоковые процессоры которые принимают и вычисляют огромные массивы данных. Передача нескольких значений в OpenGL за один раз крайне неэффективно. Чтобы хранить данные и предоставить к ним доступ OpenGL мы вводим две основные формы хранения данных -- буферы и текстуры. В этой главе мы сначала введём буферы которые являются линейными блоками не типизированных данных которые можно считать generic memory allocations. Затем, мы введём текстуры которые обычно используются для хранения многомерных данных, таких как изображение или другие типы данных.
→Chapter 5. Data
What You’ll Learn in This Chapter
• How to create buffers and textures that you can use to store data that your program can access
• How to get OpenGL to supply the values of your vertex attributes automatically
• How to access textures from your shaders for both reading and writing
What You’ll Learn in This Chapter
• How to create buffers and textures that you can use to store data that your program can access
• How to get OpenGL to supply the values of your vertex attributes automatically
• How to access textures from your shaders for both reading and writing
0 Глава 5. Данные.
Что вы изучите в этой главе
Как создавать буферы и текстуры которые вы можете использоваться для хранения данных к которым получает доступ ваша программа
Как научить OpenGL передавать значение вершинных атрибутов автоматически
Как получить доступ к текстуре из шейдера для чтения и записи
Что вы изучите в этой главе
Как создавать буферы и текстуры которые вы можете использоваться для хранения данных к которым получает доступ ваша программа
Как научить OpenGL передавать значение вершинных атрибутов автоматически
Как получить доступ к текстуре из шейдера для чтения и записи
0 void glPatchParameteri( GLenum pname, GLint value );
→Tessellation
Tessellation is the process of breaking a high-order primitive (which is known as a patch in OpenGL) into many smaller, simpler primitives such as triangles for rendering. OpenGL includes a fixed-function, configurable tessellation engine that is able to break up quadrilaterals, triangles, and lines into a potentially large number of smaller points, lines, or triangles that can be directly consumed by the normal rasterization hardware further down the pipeline. Logically, the tessellation phase sits directly after the vertex shading stage in the OpenGL pipeline and is made up of three parts: the tessellation control shader, the fixed-function tessellation engine, and the tessellation evaluation shader.
Tessellation Control Shaders
The first of the three tessellation phases is the tessellation control shader (sometimes known as simply the control shader, or abbreviated to TCS). This shader takes its input from the vertex shader and is primarily responsible for two things: the first being the determination of the level of tessellation that will be sent to the tessellation engine, and the second being the generation of data that
will be sent to the tessellation evaluation shader that is run after tessellation has occurred.
Tessellation in OpenGL works by breaking down high-order surfaces known as patches into
points, lines, or triangles. Each patch is formed from a number of control points. The number of control points per patch is configurable and set by calling glPatchParameteri() with pname set to
GL_PATCH_VERTICES and value set to the number of control points that will be used to construct each patch. The prototype of glPatchParameteri() is
Tessellation is the process of breaking a high-order primitive (which is known as a patch in OpenGL) into many smaller, simpler primitives such as triangles for rendering. OpenGL includes a fixed-function, configurable tessellation engine that is able to break up quadrilaterals, triangles, and lines into a potentially large number of smaller points, lines, or triangles that can be directly consumed by the normal rasterization hardware further down the pipeline. Logically, the tessellation phase sits directly after the vertex shading stage in the OpenGL pipeline and is made up of three parts: the tessellation control shader, the fixed-function tessellation engine, and the tessellation evaluation shader.
Tessellation Control Shaders
The first of the three tessellation phases is the tessellation control shader (sometimes known as simply the control shader, or abbreviated to TCS). This shader takes its input from the vertex shader and is primarily responsible for two things: the first being the determination of the level of tessellation that will be sent to the tessellation engine, and the second being the generation of data that
will be sent to the tessellation evaluation shader that is run after tessellation has occurred.
Tessellation in OpenGL works by breaking down high-order surfaces known as patches into
points, lines, or triangles. Each patch is formed from a number of control points. The number of control points per patch is configurable and set by calling glPatchParameteri() with pname set to
GL_PATCH_VERTICES and value set to the number of control points that will be used to construct each patch. The prototype of glPatchParameteri() is
0 Тесселяция
Тесселяция это процесс разбиения примитива ( который в OpenGL называется патч ) на множестве мелких таких как треугольники. OpenGL включает fixed-function настраиваемого движка тесселяции который способен разбить четырёхугольники, треугольники и линии во множество мелких точек, линий или треугольников которые могут быть обычно растеретизированны железом далее по всему конвейеру. Логично что фаза тесселяции идёт после вершинного шейдинга и она состоит из 3-ёх частей:
шейдер контроля тесселяции
fixed-function движок тесселяции
шейдер вычисления тесселяции.
Шейдер контроля тесселяции
Первая из 3-ёх фаз тесселяции это шейдер контроля тесселяции.
Этот шейдер принимает данные из вершинного шейдера и в первую очередь отвечает за две вещи:
Во-первых, определение уровня тесселяции который будет отправлен в движок тесселяции.
Во-вторых, генерация данных которые будут отправлены в шейдер вычисления тесселяции который запускатся после вычисления тесселяции ( видимо имеется ввиду не первая фаза шейдера, исправить прим. пер. ).
Тесселяция в OpenGL работает разбивая большие поверхности, известные как патчи, в точки, линии или треугольники. Каждый патч формируется из числа контрольных точек. Номера контрольных точек каждого патча настраивается и устанавливается вызовом glPatchParameteri() с именем установленным в GL_PATCH_VERTICES и установленным значением контрольных точек которые будут использоваться каждый патч. Прототип glPatchParameteri() это
Тесселяция это процесс разбиения примитива ( который в OpenGL называется патч ) на множестве мелких таких как треугольники. OpenGL включает fixed-function настраиваемого движка тесселяции который способен разбить четырёхугольники, треугольники и линии во множество мелких точек, линий или треугольников которые могут быть обычно растеретизированны железом далее по всему конвейеру. Логично что фаза тесселяции идёт после вершинного шейдинга и она состоит из 3-ёх частей:
шейдер контроля тесселяции
fixed-function движок тесселяции
шейдер вычисления тесселяции.
Шейдер контроля тесселяции
Первая из 3-ёх фаз тесселяции это шейдер контроля тесселяции.
Этот шейдер принимает данные из вершинного шейдера и в первую очередь отвечает за две вещи:
Во-первых, определение уровня тесселяции который будет отправлен в движок тесселяции.
Во-вторых, генерация данных которые будут отправлены в шейдер вычисления тесселяции который запускатся после вычисления тесселяции ( видимо имеется ввиду не первая фаза шейдера, исправить прим. пер. ).
Тесселяция в OpenGL работает разбивая большие поверхности, известные как патчи, в точки, линии или треугольники. Каждый патч формируется из числа контрольных точек. Номера контрольных точек каждого патча настраивается и устанавливается вызовом glPatchParameteri() с именем установленным в GL_PATCH_VERTICES и установленным значением контрольных точек которые будут использоваться каждый патч. Прототип glPatchParameteri() это
→Click here to view code image
#version 430 core
// Declare VS_OUT as an input interface block
in VS_OUT
{
vec4 color; // Send color to the next stage
} fs_in;
// Output to the framebuffer
out vec4 color;
void main(void)
{
// Simply assign the color we were given by the vertex shader
// to our output
color = fs_in.color;
}
Listing 3.6: Fragment shader with an input interface block
#version 430 core
// Declare VS_OUT as an input interface block
in VS_OUT
{
vec4 color; // Send color to the next stage
} fs_in;
// Output to the framebuffer
out vec4 color;
void main(void)
{
// Simply assign the color we were given by the vertex shader
// to our output
color = fs_in.color;
}
Listing 3.6: Fragment shader with an input interface block
0 #version 430 core
// Объявление VS_OUT как выходной блок
in VS_OUT
{
vec4 color; // Отправим цвет на следующую стадию
} fs_in;
// Вывод в буфер кадра
out vec4 color;
void main(void)
{
// Просто назначьте цвет который мы получили из вершинного шейдера
// в наш вывод
color = fs_in.color;
}
Листинг3.6: Фрагментный шейдер со входным блоком интерфейса
// Объявление VS_OUT как выходной блок
in VS_OUT
{
vec4 color; // Отправим цвет на следующую стадию
} fs_in;
// Вывод в буфер кадра
out vec4 color;
void main(void)
{
// Просто назначьте цвет который мы получили из вершинного шейдера
// в наш вывод
color = fs_in.color;
}
Листинг3.6: Фрагментный шейдер со входным блоком интерфейса
→Note that the interface block in Listing 3.5 has both a block name (VS_OUT, upper case) and an instance name (vs_out, lower case). Interface blocks are matched between stages using the block name (VS_OUT in this case), but are referenced in shaders using the instance name. Thus, modifying our fragment shader to use an interface block gives the code shown in Listing 3.6.
0 Обратите внимание что блок интерфейса в Листинге 3.5 имеет имя блока (VS_OUT, в верхнем регистре) и имя экземпляра (vs_out, в нижнем регистре). Блоки передаются между шейдерами используя имя блока ( VS_OUT в этом регистре ), но для использования в шейдерах используются имена экземпляров. Таким образом изменяя фрагментный шейдер для использования блока интерфейса даёт нам такой код показанный в Листинге 3.6.
→Click here to view code image
#version 430 core
// "offset" is an input vertex attribute
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// Declare VS_OUT as an output interface block
out VS_OUT
{
vec4 color; // Send color to the next stage
} vs_out;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
// Add "offset" to our hard-coded vertex position
gl_Position = vertices[gl_VertexID] + offset;
// Output a fixed value for vs_color
vs_out.color = color;
}
Listing 3.5: Vertex shader with an output interface block
#version 430 core
// "offset" is an input vertex attribute
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// Declare VS_OUT as an output interface block
out VS_OUT
{
vec4 color; // Send color to the next stage
} vs_out;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
// Add "offset" to our hard-coded vertex position
gl_Position = vertices[gl_VertexID] + offset;
// Output a fixed value for vs_color
vs_out.color = color;
}
Listing 3.5: Vertex shader with an output interface block
0 #version 430 core
// "offset" это входящий атрибут вершины
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// Объявление VS_OUT как выходной блок
out VS_OUT
{
vec4 color; // Отправим цвет на следующую стадию
} vs_out;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
//Прибавляем "offset" к нашим вершинным координатам
gl_Position = vertices[gl_VertexID] + offset;
// Записываем фиксированное значение в vs_color
vs_out.color = color;
}
Листинг 3.5: Вершинный шейдер с блоком интерфейса
// "offset" это входящий атрибут вершины
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// Объявление VS_OUT как выходной блок
out VS_OUT
{
vec4 color; // Отправим цвет на следующую стадию
} vs_out;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
//Прибавляем "offset" к нашим вершинным координатам
gl_Position = vertices[gl_VertexID] + offset;
// Записываем фиксированное значение в vs_color
vs_out.color = color;
}
Листинг 3.5: Вершинный шейдер с блоком интерфейса
→Interface Blocks
Declaring interface variables one at a time is possibly the simplest way to communicate data between shader stages. However, in most non-trivial applications, you may wish to communicate a number of different pieces of data between stages, and these may include arrays, structures, and other complex arrangements of variables. To achieve this, we can group together a number of variables into a n interface block. The declaration of an interface block looks a lot like a structure declaration, except that it is declared using the in or out keyword depending on whether it is an input to or output from the shader. An example interface block definition is shown in Listing 3.5.
Declaring interface variables one at a time is possibly the simplest way to communicate data between shader stages. However, in most non-trivial applications, you may wish to communicate a number of different pieces of data between stages, and these may include arrays, structures, and other complex arrangements of variables. To achieve this, we can group together a number of variables into a n interface block. The declaration of an interface block looks a lot like a structure declaration, except that it is declared using the in or out keyword depending on whether it is an input to or output from the shader. An example interface block definition is shown in Listing 3.5.
0 Блоки интерфейса
Объявление интерфейса переменных один из простых способов обмена данными между шейдерами. Тем не менее, в большинстве нетривильных программах, вы передавать множество различных данных между стадиями, и они могут содержать массивы, структуры и другие сложные механизмы переменных. Для достижения этого мы можем группировать переменные в блоке интерфейса. Объявление блока интерфейса похожа на структуру, кроме этого он объявляется вместе с ключевым словом in или out ввода или вывода из шейдера. Пример блока интерфейса определён показан в Листинге 3.5
Объявление интерфейса переменных один из простых способов обмена данными между шейдерами. Тем не менее, в большинстве нетривильных программах, вы передавать множество различных данных между стадиями, и они могут содержать массивы, структуры и другие сложные механизмы переменных. Для достижения этого мы можем группировать переменные в блоке интерфейса. Объявление блока интерфейса похожа на структуру, кроме этого он объявляется вместе с ключевым словом in или out ввода или вывода из шейдера. Пример блока интерфейса определён показан в Листинге 3.5
→Click here to view code image
#version 430 core
// Input from the vertex shader
in vec4 vs_color;
// Output to the framebuffer
out vec4 color;
void main(void)
{
// Simply assign the color we were given by the vertex shader
// to our output
color = vs_color;
}
Listing 3.4: Fragment shader with an input
#version 430 core
// Input from the vertex shader
in vec4 vs_color;
// Output to the framebuffer
out vec4 color;
void main(void)
{
// Simply assign the color we were given by the vertex shader
// to our output
color = vs_color;
}
Listing 3.4: Fragment shader with an input
0 #version 430 core
// Вводим из вершинного шейдера
in vec4 vs_color;
// Вывод в буфер кадра
out vec4 color;
void main(void)
{
// Просто назначьте цвет который мы получили из вершинного шейдера
// в наш вывод
color = vs_color;
}
Листинг 3.4: Фрагментный шейдер с вводом
// Вводим из вершинного шейдера
in vec4 vs_color;
// Вывод в буфер кадра
out vec4 color;
void main(void)
{
// Просто назначьте цвет который мы получили из вершинного шейдера
// в наш вывод
color = vs_color;
}
Листинг 3.4: Фрагментный шейдер с вводом
→As you can see in Listing 3.3, we declare a second input to our vertex shader, color (this time at location 1), and write its value to the vs_output output. This is picked up by the fragment shader of Listing 3.4 and written to the framebuffer. This allows us to pass a color all the way from a vertex attribute that we can set with glVertexAttrib*() through the vertex shader, into the fragment shader and out to the framebuffer, meaning that we can draw different colored triangles!
0 Как вы можете увидеть в Листинге 3.3, мы объявляем второй входной параметр в наш вершинный шейдер, color ( в этот раз по адресу 1 ), и записывает это значение в выходную переменную vs_output. Это подхватывает фрагментный шейдер в Листинге 3.4 и записывает во буфер кадра ( фреймбуфер прим. пер. ). Это позволяет передавать цвет на всём пути от вершинного атрибута, который мы можем установить при помощи glVertexAttrib*(), через вершинный шейдер во фрагментный и вывести его в буфер кадра, что означает что мы может рисовать разноцветные треугольники!
→Click here to view code image
#version 430 core
// "offset" and "color" are input vertex attributes
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// "vs_color" is an output that will be sent to the next shader stage
out vec4 vs_color;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
// Add "offset" to our hard-coded vertex position
gl_Position = vertices[gl_VertexID] + offset;
// Output a fixed value for vs_color
vs_color = color;
}
Listing 3.3: Vertex shader with an output
#version 430 core
// "offset" and "color" are input vertex attributes
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// "vs_color" is an output that will be sent to the next shader stage
out vec4 vs_color;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
// Add "offset" to our hard-coded vertex position
gl_Position = vertices[gl_VertexID] + offset;
// Output a fixed value for vs_color
vs_color = color;
}
Listing 3.3: Vertex shader with an output
0 #version 430 core
// "offset" и "color" are входные вершины атрибутов
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// "vs_color" выходная переменная которая будет передана
//на следующую стадию
out vec4 vs_color;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
// Прибавляем "offset" к нашим вершинным координатам
gl_Position = vertices[gl_VertexID] + offset;
// Записываем фиксированное значение в vs_color
vs_color = color;
}
Листинг 3.3: Вершинный шейдер с выходными параметрами
// "offset" и "color" are входные вершины атрибутов
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// "vs_color" выходная переменная которая будет передана
//на следующую стадию
out vec4 vs_color;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
// Прибавляем "offset" к нашим вершинным координатам
gl_Position = vertices[gl_VertexID] + offset;
// Записываем фиксированное значение в vs_color
vs_color = color;
}
Листинг 3.3: Вершинный шейдер с выходными параметрами
→When we run the program with the rendering function of Listing 3.2, the triangle will move in a smooth oval shape around the window.
Passing Data from Stage to Stage
So far, you have seen how to pass data into a vertex shader by creating a vertex attribute using the in keyword, how to communicate with fixed-function blocks by reading and writing built-in variables such as gl_VertexID and gl_Position, and how to output data from the fragment shader using the out keyword. However, it’s also possible to send your own data from shader stage to shader stage using the same in and out keywords. Just as you used the out keyword in the fragment shader to create the output variable that it writes its color values to, you can create an output variable in the vertex shader by using the out keyword as well. Anything you write to output variables in one shader get sent to similarly named variables declared with the in keyword in the subsequent stage. For example, if your vertex shader declares a variable called vs_color using the out keyword, it would match up with a variable named vs_color declared with the in keyword in the fragment shader stage (assuming no other stages were active in between).
If we modify our simple vertex shader as shown in Listing 3.3 to include vs_color as an output variable, and correspondingly modify our simple fragment shader to include vs_color as an input variable as shown in Listing 3.4, we can pass a value from the vertex shader to the fragment shader. Then, rather than outputting a hard-coded value, the fragment can simply output the color passed to it from the vertex shader.
Passing Data from Stage to Stage
So far, you have seen how to pass data into a vertex shader by creating a vertex attribute using the in keyword, how to communicate with fixed-function blocks by reading and writing built-in variables such as gl_VertexID and gl_Position, and how to output data from the fragment shader using the out keyword. However, it’s also possible to send your own data from shader stage to shader stage using the same in and out keywords. Just as you used the out keyword in the fragment shader to create the output variable that it writes its color values to, you can create an output variable in the vertex shader by using the out keyword as well. Anything you write to output variables in one shader get sent to similarly named variables declared with the in keyword in the subsequent stage. For example, if your vertex shader declares a variable called vs_color using the out keyword, it would match up with a variable named vs_color declared with the in keyword in the fragment shader stage (assuming no other stages were active in between).
If we modify our simple vertex shader as shown in Listing 3.3 to include vs_color as an output variable, and correspondingly modify our simple fragment shader to include vs_color as an input variable as shown in Listing 3.4, we can pass a value from the vertex shader to the fragment shader. Then, rather than outputting a hard-coded value, the fragment can simply output the color passed to it from the vertex shader.
0 Когда мы запустим программу с функцией рисования из Листинга 3.2, то треугольник будет двигаться по овалу в окне.
Передача Данных от Стадии к Стадии.
До сих пор вы видели как передавать данные в вершинный шейдер создавая вершинные атрибуты используя ключевое слово in, как обращаться с fixed-function блоками данных читая и записывая встроенные переменные такие как gl_VertexID и gl_Position, и как вывести данные из фрагментного шейдера используя ключевое слово out. Тем не менее также возможно отправить свои данные от шейдера к шейдеру используя те же ключевые слова ( in и out ). Так же, как вы использовали ключевое слово out во фрагментном шейдере для создания выходной переменной в которой будет записан цвет, вы можете создать out переменную в вершинном шейдере используя ключевое слово out. Всё, что вы запишите в выходные переменные в одном шейдере будет послано в одноимённые переменные объявленные с ключевым словом in в следующем шейдере на следующей стадии. Например, если ваш вершинный шейдер объявляет переменную с именем vs_color и ключевым словом out, то она совпадёт с переменной vs_color объявленной с ключевым словом in во фрагментном шейдере ( при отсутствии других этапом между этими шейдерами ).
Если мы изменим наш вершинный шейдер как показано в Листинге 3.3 добавив vs_color как выходную переменную и, соотвественно, изменив фрагментный шейдер добавив vs_color как входящую переменную как показано в Листинге 3.4, мы можем передать значение из вершинного шейдера во фрагментный. Тогда, вместо вывода жёстко запрограммированного значения, фрагментный шейдер просто может вывести цвет переданный ему из вершинного шейдера.
Передача Данных от Стадии к Стадии.
До сих пор вы видели как передавать данные в вершинный шейдер создавая вершинные атрибуты используя ключевое слово in, как обращаться с fixed-function блоками данных читая и записывая встроенные переменные такие как gl_VertexID и gl_Position, и как вывести данные из фрагментного шейдера используя ключевое слово out. Тем не менее также возможно отправить свои данные от шейдера к шейдеру используя те же ключевые слова ( in и out ). Так же, как вы использовали ключевое слово out во фрагментном шейдере для создания выходной переменной в которой будет записан цвет, вы можете создать out переменную в вершинном шейдере используя ключевое слово out. Всё, что вы запишите в выходные переменные в одном шейдере будет послано в одноимённые переменные объявленные с ключевым словом in в следующем шейдере на следующей стадии. Например, если ваш вершинный шейдер объявляет переменную с именем vs_color и ключевым словом out, то она совпадёт с переменной vs_color объявленной с ключевым словом in во фрагментном шейдере ( при отсутствии других этапом между этими шейдерами ).
Если мы изменим наш вершинный шейдер как показано в Листинге 3.3 добавив vs_color как выходную переменную и, соотвественно, изменив фрагментный шейдер добавив vs_color как входящую переменную как показано в Листинге 3.4, мы можем передать значение из вершинного шейдера во фрагментный. Тогда, вместо вывода жёстко запрограммированного значения, фрагментный шейдер просто может вывести цвет переданный ему из вершинного шейдера.
Резюме
- Пол:
- мужчина
- Родной язык:
- русский
- С нами:
-
с 14 августа 2014 г.
(4448 дней)
Не является членом клуба. - Деятельность:
-
28 версий перевода
с общим рейтингом 0
