Форум программистов, компьютерный форум, киберфорум
Наши страницы

Пишу игровой движок на C++. 023. Загрузка PNG, прозрачность.

Войти
Регистрация
Восстановить пароль
Исходные коды движка - https://github.com/532235/GoST
Документация

Другой хобби-проект в группе в вк https://vk.com/club154291467
Оценить эту запись

Пишу игровой движок на C++. 023. Загрузка PNG, прозрачность.

Запись от 532235 размещена 25.12.2017 в 03:20

Перед тем как начать проект игрульки (делая её, движок будет развиваться), нужно научиться загружать картинки с прозрачностью.

загружать png на много проще чем bmp
всё уже написано в библиотеках libpng и zlib.

Нужно просто добавить необходимые исходные коды в проект, и код этих библиотек станет частью программы.

Теперь проект будет содержать libpng-1.6.28 и zlib-1.2.11

Создаю новый плагин. gpPNG
В будущем должна быть озможность загрузки анимированных png.
В gtImage добавил поле u32 frames. Количество кадров.

Плагин gtPNG
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
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
#define GT_EXPORTS
 
#include "stdafx.h"
 
#include <memory>
#include <png.h>
#include <zlib.h>
 
#ifdef _DEBUG
#pragma comment(lib, "gost_d.lib")
#else 
#pragma comment(lib, "gost.lib")
#endif
 
extern "C"{
 
#define GT_EXT_COUNT 1u
 
    GT_API void GetPluginInfo( gtPluginInfo& info ){
        info.m_author.assign( u"532235" );
        info.m_description.assign( u"Import .png files" );
        info.m_GUID.assign( GT_GUID_IMPORT_IMAGE_PNG );
        info.m_name.assign( u"PNG file importer" );
        info.m_type = gtPluginType::import_image;
    }
 
    GT_API u32  PluginGetExtCount( void ){
        return GT_EXT_COUNT;
    }
 
    GT_API s8*  PluginGetExtension( u32 id ){
        GT_ASSERT1( id < GT_EXT_COUNT; "Bad argument", "id < 1u" );
 
        s8 * exts[ GT_EXT_COUNT ] = {
            "png"
        };
 
        return exts[ id ];
    }
 
    /* используется libpng для вывода ошибок */
    static void user_error_fn( png_structp png_ptr, png_const_charp msg ){
        gtString umsg;
        util::stringAppend( umsg, msg );
 
        gtLogWriter::printError( u"PNG Plugin: %s", umsg.data() );
 
        longjmp(png_jmpbuf(png_ptr), 1);
    }
 
    /* используется libpng для вывода предупреждений */
    static void user_warning_fn( png_structp png_ptr, png_const_charp msg ){
        gtString umsg;
        util::stringAppend( umsg, msg );
 
        gtLogWriter::printWarning( u"PNG Plugin: %s", umsg.data() );
    }
 
 
    /* Оболочка чтобы автоматически удалилось 
        png_structp png_ptr;
        png_infop info_ptr;
    */
    class PNG : public gtRefObject{
    public:
 
        PNG():
            png_ptr( nullptr ),
            info_ptr( nullptr )
        {}
 
        ~PNG(){
            if( info_ptr ){
                png_destroy_read_struct( &png_ptr, &info_ptr, NULL);
            }else if( png_ptr )
                png_destroy_read_struct( &png_ptr, NULL, NULL);
        }
 
        png_structp png_ptr;
        png_infop info_ptr;
    };
 
    /* этой функцией libpng читает файл */
    void PNGAPI user_read_fn( png_structp png_ptr, png_bytep data, png_size_t length ){
        gtFile * file = (gtFile*)png_get_io_ptr( png_ptr );
 
        png_size_t check = (png_size_t)file->read(
            (u8*)data, (u32)length );
 
        if( check != length )
            png_error( png_ptr, "Read Error" );
    }
 
        //  вызывается из gost.dll
    GT_API bool PluginLoadImage( gtImage* image, gtString* fileName ){
 
        if( !gtFileSystem::existFile( *fileName ) ){
            gtLogWriter::printWarning( u"PNG Plugin: File not exist [%s]", fileName->data() );
            return false;
        }
 
 
        {   
            gtFile_t file = util::openFileForReadBin( *fileName );
            if( !file.data() ){
                gtLogWriter::printWarning( u"PNG Plugin: Can not open file [%s]", fileName->data() );
                return false;
            }
 
 
            /* всё почти как в примере */
            #define PNG_BYTES_TO_CHECK 4
            u8 buf[PNG_BYTES_TO_CHECK];
 
            if( file->read( buf, PNG_BYTES_TO_CHECK ) != PNG_BYTES_TO_CHECK ){
                gtLogWriter::printWarning( u"PNG Plugin: Bad header [%s]", fileName->data() );
                return false;
            }
 
            if( png_sig_cmp( buf, (png_size_t)0, PNG_BYTES_TO_CHECK ) ){
                gtLogWriter::printWarning( u"PNG Plugin: Bad file [%s]", fileName->data() );
                return false;
            }
 
            gtPtr<PNG> png = gtPtrNew<PNG>( new PNG );
            png->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
              nullptr, user_error_fn, user_warning_fn);
            if( !png->png_ptr ){
                gtLogWriter::printError( u"PNG Plugin: Can not create read struct [%s]", fileName->data() );
                return false;
            }
 
 
            png->info_ptr = png_create_info_struct( png->png_ptr );
            if( !png->info_ptr ){
                gtLogWriter::printError( u"PNG Plugin: Can not create info struct [%s]", fileName->data() );
                return false;
            }
 
            if( setjmp( png_jmpbuf( png->png_ptr ) ) ){
                return false;
            }
 
            png_set_read_fn( png->png_ptr, (void *)file.data(), user_read_fn);
 
            png_set_sig_bytes( png->png_ptr, PNG_BYTES_TO_CHECK );
 
            png_read_info( png->png_ptr, png->info_ptr );
 
            u32 w, h;
            s32 bits;
            s32 color_type;
            png_get_IHDR( png->png_ptr, png->info_ptr, &w, &h, &bits, &color_type,
                NULL, NULL, NULL);
 
            if( color_type == PNG_COLOR_TYPE_PALETTE )
                png_set_palette_to_rgb( png->png_ptr );
 
            if( bits < 8 ){
                if( color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
                    png_set_expand_gray_1_2_4_to_8( png->png_ptr );
            else
                png_set_packing( png->png_ptr );
            }
 
            if( png_get_valid( png->png_ptr, png->info_ptr, PNG_INFO_tRNS ) )
                png_set_tRNS_to_alpha( png->png_ptr );
 
            if( bits == 16 )
                png_set_strip_16( png->png_ptr );
 
            if( color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
                png_set_gray_to_rgb( png->png_ptr );
 
            s32 intent;
            const f64 screen_gamma = 2.2;
 
            if( png_get_sRGB( png->png_ptr, png->info_ptr, &intent ) )
                png_set_gamma( png->png_ptr, screen_gamma, 0.45455 );
            else{
                double image_gamma;
                if( png_get_gAMA( png->png_ptr, png->info_ptr, &image_gamma ) )
                    png_set_gamma( png->png_ptr, screen_gamma, image_gamma );
                else
                    png_set_gamma( png->png_ptr, screen_gamma, 0.45455);
            }
 
            png_read_update_info( png->png_ptr,  png->info_ptr );
            png_get_IHDR( png->png_ptr, png->info_ptr, &w, &h, &bits, &color_type,
                NULL, NULL, NULL);
 
            image->width = w;
            image->height = h;
 
            if( color_type == PNG_COLOR_TYPE_RGB_ALPHA ){
                image->format = gtImage::Format::FMT_R8G8B8A8;
                image->pitch    = image->width * 4u;
            }else{
                image->format = gtImage::Format::FMT_R8G8B8;
                image->pitch    = image->width * 3u;
            }
 
 
            image->dataSize = image->pitch * image->height;
            if( !gtMainSystem::getInstance()->allocateMemory( (void**)&image->data, image->dataSize ) ){
                gtLogWriter::printWarning( u"PNG Plugin: can not allocate memory. [%s]", fileName->data() );
                return false;
            }
 
            //  указатели на строки/линии
            std::unique_ptr<png_bytep[]> row_pointers( new png_bytep[ image->height ] );
            for( u32 row = 0u, p = 0u; row < image->height; ++row ){
                row_pointers[ row ] = &image->data[ p ];
                p += image->pitch;
            }
 
            png_read_image( png->png_ptr, row_pointers.get() );
 
            png_read_end( png->png_ptr, NULL );
 
        }
 
        return true;
    }
 
}

Чтобы работала прозрачность, нужно указать это в материале.
C++
1
material.flags = gtMaterialFlag::MF_BLEND;
Прозрачная текстура может быть с мягкой прозрачностью, когда идёт смешение ранее отрисованного пикселя с текселем, либо может не рисовать участок текстуры, просто пропуская его, как бы жёсткая прозрачность.
Нужно придумать выбор.

Второй тип легко реализовать в готовом шейдере
C++
1
if( output.color.a < 0.5f ) discard;
если альфа меньше половины (127) то будет прозрачность

Для мягкой прозрачности нужно

В d3d11 плагин добавить ещё пару объектов
gtDriverD3D11.h
C++
1
2
3
ID3D11BlendState*       m_blendStateAlphaEnabled;
ID3D11BlendState*       m_blendStateAlphaDisabled;
void    enableBlending( bool );
Нужно создать blend state
gtDriverD3D11::initialize
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
D3D11_BLEND_DESC  bd;
    memset( &bd, 0, sizeof(bd) );
    bd.RenderTarget[0].BlendEnable = TRUE;
    bd.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
    bd.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
    bd.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
    bd.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
    bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
    bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
    bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
    bd.RenderTarget[0].RenderTargetWriteMask = 0x0f;
 
    if( FAILED( m_d3d11Device->CreateBlendState( &bd, &m_blendStateAlphaEnabled ) ) ){
        gtLogWriter::printError( u"Can't create Direct3D 11 blend state" );
        return false;
    }
 
    bd.RenderTarget[0].BlendEnable = FALSE;
    
    if( FAILED( m_d3d11Device->CreateBlendState( &bd, &m_blendStateAlphaDisabled ) ) ){
        gtLogWriter::printError( u"Can't create Direct3D 11 blend state" );
        return false;
    }
 
    enableBlending( true );
C++
1
2
3
4
5
6
7
8
9
10
11
12
void    gtDriverD3D11::enableBlending( bool b ){
    float blendFactor[4];
    blendFactor[0] = 0.0f;
    blendFactor[1] = 0.0f;
    blendFactor[2] = 0.0f;
    blendFactor[3] = 0.0f;
    if( b ){
        m_d3d11DevCon->OMSetBlendState( m_blendStateAlphaEnabled, blendFactor, 0xffffffff );
    }else{
        m_d3d11DevCon->OMSetBlendState( m_blendStateAlphaDisabled, blendFactor, 0xffffffff );
    }
}
Если в шейдеру убрать предложение с discard то всё будет работать как надо.
На скриншоте четвёртая полоска сверху это и есть полоса с прозрачностью.
Она смешивается с красным фоном.

Потом нужно подумать, как разделить жёсткую прозрачность и мягкую.
Для мягкой нужно включать blend state, и рисовать объекты в последнюю очередь.
Для жесткой ничего включать не нужно, и ппорядок рисования не важен.

По этому либо делаю отдельный шейдер 2D_Transparent, либо оба метода совмещаю в одном шейдере, и в материале настраиваю уровень, на каком альфа значении применять discard.
Пусть будет пока второй вариант.
hlsl
C++
1
2
3
4
5
6
cbuffer cbPixel{
    float opacity;
    float3 padding;
};
...
if( output.color.a < opacity ) discard;
gtDriverD3D11::initialize
C++
1
if( !m_shader2DStandart->createShaderObject( 16u ) ) return false;
Функция рисования (на данном этапе пока такая, пока нет времени занятся конкретно шейдерами, нужно сделать сой коллбэк для каждого шейдера, и уже внутри него всё настраивать)
кусок кода рисования
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
u32 sz = ((gtShaderImpl*)shader)->m_constantBuffers.size();
 
    struct cbPixel{
        float opacity;
        float padding[3];
    }cb_pixel;
 
    cb_pixel.opacity = 1.f - material.opacity;
 
    void * cbs[2] = 
    {
        &cb,
        &cb_pixel
    };
 
    for( u32 i = 0u; i < sz; ++i ){
        D3D11_MAPPED_SUBRESOURCE mappedResource;
        
        m_d3d11DevCon->Map(
            ((gtShaderImpl*)shader)->m_constantBuffers[ i ],
            0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
 
        D3D11_BUFFER_DESC d;
        ((gtShaderImpl*)shader)->m_constantBuffers[ i ]->GetDesc( &d );
 
        memcpy( mappedResource.pData, cbs[ i ], d.ByteWidth );
        m_d3d11DevCon->Unmap( ((gtShaderImpl*)shader)->m_constantBuffers[ i ], 0 );
 
    }
    m_d3d11DevCon->VSSetConstantBuffers( 0, 1, &((gtShaderImpl*)shader)->m_constantBuffers[ 0u ] );
    m_d3d11DevCon->PSSetConstantBuffers( 0, 1, &((gtShaderImpl*)shader)->m_constantBuffers[ 1u ] );
 
    for( u32 i = 0u; i < 16u; ++i ){
        if( !material.textureLayer[ i ].texture ) break;
 
        gtTextureD3D11* texture = (gtTextureD3D11*)material.textureLayer[ i ].texture;
 
        m_d3d11DevCon->PSSetShaderResources( i, 1, texture->getResourceView() );
        m_d3d11DevCon->PSSetSamplers( i, 1, texture->getSamplerState() );
    }
 
    /* так делать плохо, но пусть пока будет так */
    if( material.flags & gtMaterialFlag::MF_BLEND )
        enableBlending( true );
    else
        enableBlending( false );
    
    m_d3d11DevCon->Draw( 6, 0 );


TEST
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
gtImage * image1 = my_system->loadImage( gtFileSystem::getProgramPath() + u"png8.png" );
    gtImage * image2 = my_system->loadImage( gtFileSystem::getProgramPath() + u"png24.png" );
    gtImage * image3 = my_system->loadImage( gtFileSystem::getProgramPath() + u"png32.png" );
    gtImage * image4 = my_system->loadImage( gtFileSystem::getProgramPath() + u"png48.png" );
    
    gtPtr<gtTexture> texture1 = gtPtrNew<gtTexture>( driver1->createTexture( image1 ) );
    gtPtr<gtTexture> texture2 = gtPtrNew<gtTexture>( driver1->createTexture( image2 ) );
    gtPtr<gtTexture> texture3 = gtPtrNew<gtTexture>( driver1->createTexture( image3 ) );
    gtPtr<gtTexture> texture4 = gtPtrNew<gtTexture>( driver1->createTexture( image4 ) );
 
    /* software картинка больше не нужна */
    my_system->removeImage( image1 );
    my_system->removeImage( image2 );
    my_system->removeImage( image3 );
    my_system->removeImage( image4 );
    
    gtMaterial material1;
    material1.flags = gtMaterialFlag::MF_BLEND;
    material1.opacity = 1.f;
    material1.textureLayer[ 0u ].texture = texture1.data();
 
    gtMaterial material2;
    //material2.flags = gtMaterialFlag::MF_BLEND;
    material2.opacity = 0.5f;/*картинка 24 bit, без альфа, не сработает*/
    material2.textureLayer[ 0u ].texture = texture2.data();
 
    gtMaterial material3;
    material3.flags = gtMaterialFlag::MF_BLEND;
    material3.opacity = 0.5f;
    material3.textureLayer[ 0u ].texture = texture3.data();
 
    gtMaterial material4;
    //material4.flags = gtMaterialFlag::MF_BLEND;
    material4.opacity = 0.1f;
    material4.textureLayer[ 0u ].texture = texture4.data();
 
    
    while( my_system->update() ){
 
        
        if( driver1 ){
            driver1->beginRender(true,gtColor(1.f,0.f,0.f,1.f));
 
            driver1->draw2DImage( v4i({ 0, 0, 400, 300 }), material1 );
            driver1->draw2DImage( v4i({ 400, 0, 800, 300 }), material2 );
            driver1->draw2DImage( v4i({ 0, 300, 400, 600 }), material3 );
            driver1->draw2DImage( v4i({ 400, 300, 800, 600 }), material4 );
 
            driver1->endRender();
        }
 
    }
Следующий пост будет про клавиатуру и мышь.
Миниатюры
Нажмите на изображение для увеличения
Название: ew.jpg
Просмотров: 171
Размер:	59.7 Кб
ID:	4572  
Размещено в Игровой движок
Просмотров 194 Комментарии 0
Всего комментариев 0

Комментарии

 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.