Jump to content
  • Advertisement
daniel hernandez

How to make 8-bit sprites out of 16 or 32-bit ones?

Recommended Posts

Greetings everyone, i am new around here. I came here to see if anyone could help me with making sprites, a full sprite sheet, etc.

As the title says, i want to be able to make 8-bit sprites from existing sprites, but are either 16 or 32-bit. Take for example these two images: https://imgur.com/a/zfutT

How do i "convert" or re-create them in 8-bit?

Share this post


Link to post
Share on other sites
Advertisement

The issue I see with your question is...what defines 16/32-bit as far as sprites go?  I'm guessing you are referring to the limitations some systems had, for example, the PS1 sprites are sometimes considered 32bit, SNES/Genesis as 16bit, and then NES as 8bit.  If that is the case, it would be good to specify.

The NES sprites had limits in both the palette they could choose from, and the amount of colors that could actually be used for each sprite.  If I remember correctly, the palette had around 56 colors(and was lacking in certain colors, like browns), and you could only have 3 colors in use for a single sprite(with the "4th" color being transparency).  If you want to lower "bits" of more modern sprites, a good place to start is simply using less colors per sprite.  You could also find or create a small palette to choose from for all the sprites in a game(or art piece or whatever).  The other thing to consider is lowering the resolution, and a good resolution for "8bit" sprites would be 16x16 in my opinion.

The final catch with this...I don't think there is going to be a good automatic way to do this at all.  You technically could lower resolution in an art program, but details will be lost and will need to be rebuilt(if the new resolution has space).  And you could do some kind of color amount reduction, but I've never seen good results come from that either, at least not with the amount of colors under discussion.

Share this post


Link to post
Share on other sites

The colors aren't much of an issue for the moment, i know how to handle that. what i do want to know is how to make the sprites look "smaller" in simple terms, so they can "fit" in a NES game. For example: using a variation of the 32 bits image i gave you earlier, take a look at this one: https://imgur.com/a/5WRTv

The image from the left is made for the game boy color (or the normal game boy? i don't know) but it has obviously less details than the image of the right, and is also smaller.

 

Sorry if those things i mentioned doesn't define what a 8/16/32 bits sprite is, as i don't know much on the matter.

 

EDIT: Also, credits goes for devianart user MegaRed225 for the image used on the left.

Edited by daniel hernandez

Share this post


Link to post
Share on other sites

I still am much confused what problem you are trying to solve here. To eliminate the confusion, please only talk about the image, and not about systems that may display it.

An image has a size in pixels (in 2 directions), and each pixel has a colour, usually in 3 or 4 channels (normally RGB and an A channel for opaqness). Each channel has a bit depth (number of bits available to express the fraction of eg Red). Further limitations may be that only a limited number of unique RGB combinations may be used (eg gif has 256 colours available from the 24bit RGB space), or that the set of available RGB combinations is fixed (eg the system has already fixed the palette).

EDIT: For valid sizes, you may want to use cropped image sizes, eg not the large white area that you have in some of the examples, as that is non-relevant to the problem.

 

Since you mostly excluded reduction in colours from the question, I assume you are talking about a reduction in size. You can map several pixels from the source image onto one pixel in the destination, by blending their colours.

As an example, converting an image from 10x1 to 5x1 can be done by blending the colours of the first two pixels into the first pixel of the destination, the colours of the 3rd and 4th pixel into the 2nd pixel at the destination, and so on.

The reduction doesn't have to be a power of 2, if you go from 10 pixels to 6 pixels, each pixel in the destination gets 10/6th pixels from the source, using a weighted average, for example. Enlarging is the same process, except you'll very little colour information available.

 

None of these automated techniques will give you good quality images. The computer doesn't understand that one line has more importance than a filled area near to it, for example. In any case, you'll need to improve, or even redraw the images, if you want good quality.

 

Edited by Alberth
clarified that uncropped image size may be confusing too

Share this post


Link to post
Share on other sites
7 hours ago, Alberth said:

As an example, converting an image from 10x1 to 5x1 can be done by blending the colours of the first two pixels into the first pixel of the destination, the colours of the 3rd and 4th pixel into the 2nd pixel at the destination, and so on.

The reduction doesn't have to be a power of 2, if you go from 10 pixels to 6 pixels, each pixel in the destination gets 10/6th pixels from the source, using a weighted average, for example. Enlarging is the same process, except you'll very little colour information available.

 

First of all, sorry for all the confusion. I understand very little about this, so most if not all my explanations lacks the "definition" of what sprite making is...

As for your answer, Yes, i want to create them myself, as stated in my first post. If an automatic process gives a bad quality image, then how can i do all of that manually?

Share this post


Link to post
Share on other sites
22 minutes ago, daniel hernandez said:

As for your answer, Yes, i want to create them myself, as stated in my first post. If an automatic process gives a bad quality image, then how can i do all of that manually?

You'll have to draw them manually, using an image editor (e.g. paint.net -- there might be better suited pixel graphic editors I'm not familiar with).

Instead of starting each image from scratch, I would suggest loading up the original image and resizing it to the intended size, and saving that as your starting point. Using the resized image as your base and the original image as a reference, make changes until you're happy with the result.

For more general advice on creating any sprites, I would recommend searching online for sprite-making resources.

Share this post


Link to post
Share on other sites
Hi, yes I think the original question was really saying ‘How do I convert modem created sprites to look like they belong in an 8bit system’  RATHER than ‘How do I make 8 bit sprites from 32 bit ones’.
If so, this is more of a art type question.  I’m not an artist, but out of interest I took one of your images you supplied (the right one in 5WRTv) then In Gimp software I did this:-
-Reduced the scale by 50%,
-Converted the image to ‘Indexed mode’ ( from RGB) and set the colour palette size to 4.
-I then converted back to RGB mode
-I then did a hue and saturation shift to make the colour match your game boy version.
I ended up with this (see attachment, left image).  As mentioned, you would then need an artist to make it look better.

If I may bore you with some technical information because I think you may be a bit confused.  The term ‘8 bit’ on a ‘8 bit system’ is actually referring to the size of the ‘data bus’ that connects the CPU to the RAM, and ABSOLUTELY nothing to do with the format or resolution of the sprites on the system.
8 bit (1 byte) per pixel graphics really means a sprite which can use up to 256 palette colours.  Typically old ‘8 bit systems’ only had a maximum palette size of 4 to 64 (depending on what system).
Ironically the modern looking image I converted above was already less than 256 colours so could of already been stored as 8 bit (1 byte per pixel) raw format image file.
 
Hope this has helped.

converted2.jpg

Edited by desiado

Share this post


Link to post
Share on other sites

I see... Sorry for confusing matters.

 

So, i take it that this forum doesn't deal with art questions, then? because i have one more... But i'm beginning to think that it doesn't belong here.

Share this post


Link to post
Share on other sites

There is a visual art forum, you may want to try it there.

While beginners forum is for anything, almost all questions here are related to programming, and most readers are programmers. Together with a confusing question about bith depths of images, we (in hindsight incorrectly) considered your question to be a programming question about image formats rather than "make it look like a retro image".

 

Share this post


Link to post
Share on other sites

  • Advertisement
  • Advertisement
  • Popular Tags

  • Popular Now

  • Advertisement
  • Similar Content

    • By lucky6969b
      like this?

      const D3DXMATRIX robertx = D3DXMATRIX(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
    • By bojanzarnoski@gmx.de
      Hello,
      I want to get into coding again by programming a 2D platformer to get started, but i don't know if i should use Java or C# with the unity engine.
      I am pretty fit with Java, but with c# i have to start from scratch. What do you recommend and why?
    • By 3dmodelerguy
      So I have this code to create a textbox ui element with SDL2 and for the most part it works:
      #include <iostream> #include "TextboxUi.hpp" #include "../Engine/Text.hpp"; #include "../Utility/SdlUtility.hpp"; #include "../Engine/Game.hpp"; TextboxUi::TextboxUi(int x, int y, int width, int height, SDL_Color backgroundColor, SDL_Color textColor) { _background = {x, y, width, height}; _backgroundColor = backgroundColor; _textColor = textColor; } void TextboxUi::setValue(std::string value) { _value = value; } void TextboxUi::handleInput(SDL_Event &event) { switch (event.type) { case SDL_TEXTINPUT: { if (_isActive) { _value += event.text.text; _cursorPosition += 1; } break; } case SDL_TEXTEDITING: { break; } case SDL_KEYUP: { switch (event.key.keysym.sym) { case SDLK_BACKSPACE: if (_value.size() == 0) { break; } _cursorPosition -= 1; _value.erase(_cursorPosition, 1); break; case SDLK_s: { if (!_isActive) { _isActive = true; } break; } case SDLK_LEFT: { if (_cursorPosition == 0) { break; } _cursorPosition -= 1; break; } case SDLK_RIGHT: { if (_cursorPosition == _value.size()) { break; } _cursorPosition += 1; break; } case SDLK_ESCAPE: _isActive = false; _value = ""; break; default: break; } break; } default: break; } } void TextboxUi::enable() { _isActive = true; } void TextboxUi::disable() { _isActive = false; } void TextboxUi::draw(SDL_Renderer* renderer) { SdlUtility::setRenderDrawColor(Game::renderer, _backgroundColor); SDL_RenderFillRect(Game::renderer, &_background); // if there is no text Text would fail to create the surface and texture (not quite sure why) if (_value.size() == 0) { return; } Text text = Text("Assets/Fonts/Vera.ttf", 12, _value, _textColor); text.display(_background.x + 5, _background.y + 2); // @todo implement visual for text cursor } The one last thing I want to do before I call this textbox done for the time being is try to figure out how to simulate a cursor. At this point just getting a solid cursor would be final (though ultimately I would like to have it blinking. I have the cursor position so I know between which characters the cursor should be however I am not sure how to calculate that.
      Is there a way to calculate the width of a given string without actually rendering said sting to the screen with SDL2 / SDL2_ttf?
    • By DevAndroid
      Hello everyone,
      I'm trying to display a 2D texture to screen but the rendering isn't working correctly.
      First of all I did follow this tutorial to be able to render a Text to screen (I adapted it to render with OpenGL ES 2.0) : https://learnopengl.com/code_viewer.php?code=in-practice/text_rendering
      So here is the shader I'm using :
      const char gVertexShader[] = "#version 320 es\n" "layout (location = 0) in vec4 vertex;\n" "out vec2 TexCoords;\n" "uniform mat4 projection;\n" "void main() {\n" " gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);\n" " TexCoords = vertex.zw;\n" "}\n"; const char gFragmentShader[] = "#version 320 es\n" "precision mediump float;\n" "in vec2 TexCoords;\n" "out vec4 color;\n" "uniform sampler2D text;\n" "uniform vec3 textColor;\n" "void main() {\n" " vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\n" " color = vec4(textColor, 1.0) * sampled;\n" "}\n"; The render text works very well so I would like to keep those Shaders program to render a texture loaded from PNG.
      For that I'm using libPNG to load the PNG to a texture, here is my code :
      GLuint Cluster::loadPngFromPath(const char *file_name, int *width, int *height) { png_byte header[8]; FILE *fp = fopen(file_name, "rb"); if (fp == 0) { return 0; } fread(header, 1, 8, fp); if (png_sig_cmp(header, 0, 8)) { fclose(fp); return 0; } png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { fclose(fp); return 0; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); fclose(fp); return 0; } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); fclose(fp); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); return 0; } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); int bit_depth, color_type; png_uint_32 temp_width, temp_height; png_get_IHDR(png_ptr, info_ptr, &temp_width, &temp_height, &bit_depth, &color_type, NULL, NULL, NULL); if (width) { *width = temp_width; } if (height) { *height = temp_height; } png_read_update_info(png_ptr, info_ptr); int rowbytes = png_get_rowbytes(png_ptr, info_ptr); rowbytes += 3 - ((rowbytes-1) % 4); png_byte * image_data; image_data = (png_byte *) malloc(rowbytes * temp_height * sizeof(png_byte)+15); if (image_data == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); return 0; } png_bytep * row_pointers = (png_bytep *) malloc(temp_height * sizeof(png_bytep)); if (row_pointers == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); free(image_data); fclose(fp); return 0; } int i; for (i = 0; i < temp_height; i++) { row_pointers[temp_height - 1 - i] = image_data + i * rowbytes; } png_read_image(png_ptr, row_pointers); GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, GL_ZERO, GL_RGB, temp_width, temp_height, GL_ZERO, GL_RGB, GL_UNSIGNED_BYTE, image_data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); free(image_data); free(row_pointers); fclose(fp); return texture; } This code just generates the texture and I store the id on memory
      And then I want to display my texture on any position (X, Y) of my screen so I did the following (That's works, at least the positioning).
      //MY TEXTURE IS 32x32 pixels ! void Cluster::printTexture(GLuint idTexture, GLfloat x, GLfloat y) { glActiveTexture(GL_TEXTURE0); glBindVertexArray(VAO); GLfloat vertices[6][4] = { { x, y + 32, 0.0, 0.0 }, { x, y, 0.0, 1.0 }, { x + 32, y, 1.0, 1.0 }, { x, y + 32, 0.0, 0.0 }, { x + 32, y, 1.0, 1.0 }, { x + 32, y + 32, 1.0, 0.0 } }; glBindTexture(GL_TEXTURE_2D, idTexture); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferSubData(GL_ARRAY_BUFFER, GL_ZERO, sizeof(vertices), vertices); glBindBuffer(GL_ARRAY_BUFFER, GL_ZERO); glUniform1i(this->mTextShaderHandle, GL_ZERO); glDrawArrays(GL_TRIANGLE_STRIP, GL_ZERO, 6); } My .png is a blue square.
      The result is that my texture is not loaded correctly. It is not complete and there are many small black spots. I don't know what's going on ? It could be the vertices or the load ? Or maybe I need to add something on the shader. I don't know, I really need help.
      Thanks !
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!