FlailingFog / KK-Blender-Porter-Pack

Plugin pack for exporting Koikatsu characters to Blender.
308 stars 29 forks source link

Automatically get and set material colors #22

Closed TheMaskedMan00 closed 2 years ago

TheMaskedMan00 commented 2 years ago

in game you can use material inspector to view all values for colors and stuff, so can you make it when you import your textures a gui will pop up asking you for numbers/colors for each material, which correspond the the material inspector. so you just enter them and press ok and it will automatically fix all the colors for you

FlailingFog commented 2 years ago

It would be a neat idea to automatically get the color values from koikatsu, but the game applies a saturation filter (called Amplify Color) to everything on screen. Directly reading the raw values into blender will give you duller, lighter versions of each color because they aren't going through this saturation process. In order for this to work, each color would need to be converted.

There are tools like https://github.com/ManlyMarco/KK_BetterColorPicker and https://koikoi.happy.nu/texture_color_adjuster/ that use the rgb values and a lookup texture to UNsaturate a color with code showing step by step how it's done, but I have no idea how the actual lookup process works, so I don't know if this process can used in reverse to generate the saturated version of the color.

Amplify Color is now a free asset on the unity store, but I don't know if the code or process for the package is even available to view so it can be recreated in blender. The LUTs that the game uses are at least available in the BetterColorPicker repository above, so it's really just the process that needs to be figured out.

So without Amplify Color's code, and without a similar saturation process, there'd be no point in trying to do this. The next best thing is the current process, where you manually click on a screenshot that already has the saturation filter applied.

MediaMoots commented 2 years ago

Here's a script that converts KK RGB values to its saturated values. image

The way it works is it uses a LUT taken from KK and saturates the colors.

  1. Set colorToSample to the RGB values you want to convert.
  2. Make sure to add the .cube file to the script
  3. The converted RGB values will be printed to the log

To use the script in Blender you will need to install the dependencies to blender's python installation. If so, make sure to run python as admin so that the packages will be installed the blender's install folder.

image The image on the right is the corrected color.

Here is the .cube LUT KKV2.zip

Dependencies: https://colour.readthedocs.io/en/develop/index.html#installation https://pillow.readthedocs.io/en/stable/installation.html numpy

from colour import read_LUT
from PIL import Image
import numpy as np

############################################# CONFIG

colorToSample = (255,255,255)
samplerSize = 16

#############################################

# Get KK.cube LUT
lut3d = read_LUT("W:/KK/KKV2.cube")

# Create an image sample and scale it
imgSampler = np.array(Image.new('RGB', (samplerSize, samplerSize), color=colorToSample))/255

# Apply KK lut3d to the sampler
imgSampler = lut3d.apply(imgSampler)

# Scale the converted sampler
imgSampler = np.uint8(imgSampler*255)

# Store the converted RGB values
convertedRGB = imgSampler[0][0]

print(convertedRGB)

# For debug
#PIL_image = Image.fromarray(imgSampler)
#PIL_image.show()
MediaMoots commented 2 years ago

New LUT It seems to be slightly more accurate.

KKV3.zip

MediaMoots commented 2 years ago

Super-Duper Ultimate KK Color Reproduction Script: Version 4

This took over 60 hours to R&D...

What's new?

Call 1800-555 and double your offer, buy one get one free!!! Just kidding.

But yes, this is the ultimate script. Just set the color in the Config section and get the corrected value out. It's all contained in a function that you can copy and paste.

How to use:

  1. Download this LUT: KK LUT V4.zip
  2. Import it into blender image
  3. Set (In the script) colorToUse and make sure lutName is correct
  4. Profit

Super-Duper Ultimate KK Color Reproduction Script: Version 4

import bpy
import bgl
import gpu
import numpy as np
from gpu_extras.batch import batch_for_shader

# Please below to change the settings

########## FUNCTIONS ##########

def colorToKK(color, lutName):
    width = 1
    height = 1

    # Some Sauce
    vertex_default = '''
    in vec2 a_position;

    void main() {
        gl_Position = vec4(a_position, 0.0, 1.0);
    }
    '''

    # The Secret Sauce
    current_code = '''
    uniform vec3 inputColor;
    uniform sampler2D lut;

    vec3 to_srgb(vec3 c){
        c.rgb = max( 1.055 * pow( c.rgb, vec3(0.416666667,0.416666667,0.416666667) ) - 0.055, 0 );
        return c;
    }

    void main() {
        vec3 color = inputColor / 255;

        const vec3 coord_scale = vec3(0.0302734375, 0.96875, 31.0);
        const vec3 coord_offset = vec3( 0.5/1024, 0.5/32, 0.0);
        const vec2 texel_height_X0 = vec2( 0.03125, 0.0 );

        vec3 coord = color * coord_scale + coord_offset;

        vec3 coord_frac = fract( coord );
        vec3 coord_floor = coord - coord_frac;
        vec2 coord_bot = coord.xy + coord_floor.zz * texel_height_X0;
        vec2 coord_top = coord_bot + texel_height_X0;

        vec3 lutcol_bot = texture( lut, coord_bot ).rgb;
        vec3 lutcol_top = texture( lut, coord_top ).rgb;

        vec3 lutColor = mix(lutcol_bot, lutcol_top, coord_frac.z);

        lutColor = to_srgb(lutColor);

        vec3 shaderColor = lutColor;

        gl_FragColor = vec4(shaderColor.rgb, 1);
    }
    '''

    # This object gives access to off screen buffers.
    offscreen = gpu.types.GPUOffScreen(width, height)

    # Context manager to ensure balanced bind calls, even in the case of an error.
    # Only run if valid
    with offscreen.bind():

        # Clear buffers to preset values
        bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)

        # Initialize the shader
        # GPUShader combines multiple GLSL shaders into a program used for drawing. 
        # It must contain a vertex and fragment shaders, with an optional geometry shader.
        shader = gpu.types.GPUShader(vertex_default, current_code)

        # Initialize the shader batch
        # It makes sure that all the vertex attributes necessary for a specific shader are provided.
        batch = batch_for_shader(
            shader, 
            'TRI_FAN', {
                'a_position': ((-1, -1), (1, -1), (1, 1), (-1, 1))
            },
        )

        # Bind the shader object. Required to be able to change uniforms of this shader.
        shader.bind()

        try:
            # Specify the value of a uniform variable for the current program object. 
            # In this case, a color tuple.
            shader.uniform_float('inputColor', colorToUse)
        except ValueError:
            pass

        try:
            lutImage = bpy.data.images[lutName]

            # https://docs.blender.org/api/current/bgl.html
            bgl.glBindTexture(bgl.GL_TEXTURE_2D, lutImage.bindcode)
            bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_WRAP_S, bgl.GL_CLAMP_TO_EDGE)
            bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_WRAP_T, bgl.GL_CLAMP_TO_EDGE)
            bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR)
            bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR)

            # Specify the value of a uniform variable for the current program object. 
            # In this case, an image.
            shader.uniform_int("lut", 0)
        except ValueError: 
            pass

        # Run the drawing program with the parameters assigned to the batch.
        batch.draw(shader)

        # The Buffer object is simply a block of memory that is delineated and initialized by the user.
        buffer = bgl.Buffer(bgl.GL_BYTE, width * height * 3)

        # Select a color buffer source for pixels.
        bgl.glReadBuffer(bgl.GL_BACK)

        # Read a block of pixels from the frame buffer.
        bgl.glReadPixels(0, 0, width, height, bgl.GL_RGB, bgl.GL_UNSIGNED_BYTE, buffer)

    # Free the offscreen object. The framebuffer, texture and render objects will no longer be accessible.
    offscreen.free()

    # Get and return the pixels from the final buffer
    finalColor = [v for v in buffer]
    finalColor = np.array(finalColor).reshape(width, height, -1)
    return finalColor[0][0]

########## CONFIG ##########

# Set the RGB values from KK here.
colorToUse = (127, 127, 127)

# Make sure to import the LUT into blender first, then set it's name here.
lutName = "KK LUT V4.png"

convertedColor = colorToKK(colorToUse, lutName)
# For Debugging
print(convertedColor)

Here's a crude snippet to set a RGB node's color:

convertedColor = colorToKK(colorToUse, lutName)

floatColor = convertedColor / 255

mat = bpy.data.materials['Material']
mat.use_nodes = True
nodes = mat.node_tree.nodes
colorNode = nodes['RGB']
colorNode.outputs[0].default_value = (floatColor[0], floatColor[1], floatColor[2], 1)

See how the retuned RGB needs to be divided by 255 for it to be compatible with Blender's RGB system. To see the correct colors in the viewport, make sure View Transform is set to Raw (I may be wrong here, but that's what worked for me.): image

@FlailingFog I strongly suggest using this script instead of the older version for V5/V6 If you do use it, please credit me.

FlailingFog commented 2 years ago

@MediaMoots
I-I kneel...

I just got to trying your first script last night, and already thought it did a really good job. This second one looks like literal black magic though... How the hell did you do this? It looks like you're running C code inside of a python script...

Still, this is cool stuff, thank you for doing this! It opens up possibilities for (un)converting main textures for accessories, clothing, and studio items too. Since your new script is using the actual KK LUT, can you use the night time LUT to give you the dark (shadow) version of each color as well?

The final piece of this puzzle would be to automate the color value collection itself inside of koikatsu. Luckily the code for this is already written, because the KK Material Editor plugin accesses these values. If that code could be repurposed to save each material's color value to a csv or something, the csv could be read into blender, run through your conversion code and applied to all of the material templates automatically. Any main textures could also be converted the same way. I'll definitely be looking into making this after I finish some other stuff for V5. Ideally I'd want your code and this color collection thing to appear side by side, so not sure if it will appear in V5 or V6. It depends on how easy repurposing the material editor code turns out to be.

Could you make a PR to the V5 branch with this code and put it in the 'importing' or 'extras' directory? Pretty sure you'll be credited as the creator of the file that way.

(You might also want to share your code on one of the koikatsu discord modding channels; there might be interest in the color conversion process for people making modded items.)

MediaMoots commented 2 years ago

@FlailingFog Haha, thanks.

Since your new script is using the actual KK LUT, can you use the night time LUT to give you the dark (shadow) version of each color as well?

Yes, every LUT KK has can be used. Here's a zip with three of them (Day (Same as the one from above), Sunset, Night) LUTs.zip

The final piece of this puzzle would be to automate the color value collection itself inside of koikatsu.

That's a good idea. Would you like some help to do this?

Could you make a PR to the V5 branch with this code and put it in the 'importing' or 'extras' directory? Pretty sure you'll be credited as the creator of the file that way.

Ok, sounds good.

MediaMoots commented 2 years ago

Luckily the code for this is already written, because the KK Material Editor plugin accesses these values.

So, I did a bit of research and found that the Material Editor can't get the color values for many items, such as the skin and clothes.

MediaMoots commented 2 years ago

I think I got it, the idea is to parse and get the color data from character cards.

If you like, I can work on that and update you if there's progress.

FlailingFog commented 2 years ago

So, I did a bit of research and found that the Material Editor can't get the color values for many items, such as the skin and clothes.

Shoot you're right, it only seems to give color values for accessories. I could have sworn it gave more than that... There goes that plan I guess.

I think I got it, the idea is to parse and get the color data from character cards.
If you like, I can work on that and update you if there's progress.

Neat, I didn't know you could do that. If you're willing to do it, then please do!

MediaMoots commented 2 years ago

Y'know, after digging s'more, maybe generating a csv/json template for ppl to fill in manually might be better, haha What do you think?

MediaMoots commented 2 years ago

So, I made a script that generates a CSV template that matches the color boxes available (Except some paint colors) in KK

This is the result: ColorTemplate.csv

Do you think that with this new color workflow, the shaders might need to be rewritten? Such as how it handles skin color, clothes top/bot, and more?

FlailingFog commented 2 years ago

I don't know... If the color values can't be compiled automatically then I still feel like clicking on a screenshot would be faster than finding, recording and retyping each RGB value in. Is there like a function you can use in koikatsu that just lists them out on one page for easy recording?

I looked into https://bitbucket.org/Joan6694/hsplugins/src/master/RendererEditor/ but like material editor, it doesn't seem to give every color value.

I tried this and it looks like it gives skin/hair/etc color values in the dump, but there doesn't seem to be much text associated with the accessory colors, so I don't know how those colors could be correlated with the correct materials: https://koikoi.happy.nu/json_dump/

Might be kind of janky, but maybe this dumper could be used for the values material editor can't get, and the material editor code snippets could be used to get the rest? The json dumper has good labels for everything, so it should be easy to know what goes where, and material editor should know the name of the accessory it's getting color values for, so it could be matched up with the material name shown in blender.

Do you think that with this new color workflow, the shaders might need to be rewritten?

I don't think anything in the blend file would have to be changed, the code reading these values just has to load the colors into the correct spots.

MediaMoots commented 2 years ago

Hi @FlailingFog

I think I have a completely different and easy (for the user) solution.

But first, what do you imagine the workflow to be like?

Import mesh -> Cleanup -> Import Shaders / Materials -> Correct colors -> etc... Like that?

MediaMoots commented 2 years ago

Quick question, why do the Nipples need 4 color values?

Thanks.

FlailingFog commented 2 years ago

That's how it is now, but the workflow can change if it makes it easier.

Some nipple textures are black and white but others have RGB components, so that's where the first three come from. I remember getting better results with an additional fourth color though, so I swapped to that. Nipple 1 and Nipple 2 (combined) can be treated as the red component of the nipple texture, while 3 and 4 represent the green and blue components.

MediaMoots commented 2 years ago

Ok cool, thanks for the explanation.

This (Nipple colors) will be an issue though, since KK only needs one color, so I'm not sure how to translate that over to blender.

MediaMoots commented 2 years ago

Also, I may be wrong but the eyeline shader appears to be incorrect. image image

See how in KK the eyeline is a gradient (from black to color), as opposed to a solid color in blender.

Texture: image

MediaMoots commented 2 years ago

Ok cool, thanks for the explanation.

This (Nipple colors) will be an issue though, since KK only needs one color, so I'm not sure how to translate that over to blender.

nvm, i got this one

MediaMoots commented 2 years ago

PR #31

FlailingFog commented 2 years ago

This looks awesome! Really smart idea grabbing colors from the pmx images. I'll check this out and also fix the eyeline shader. I know I had the second color at one point, so I wonder what happened to it...

MediaMoots commented 2 years ago

Found a weird quirk in KK The eyebrows have some kind of saturation going on image

See how the colors are completely different shades? Actually, they are set to the same color. image image

Weird stuff.

MediaMoots commented 2 years ago

@FlailingFog The new eyeline system still has an issue. The fade is also off. In KK image In KKBP: image

Same color.

Example with both colors (KK and KKBP) set to white KK image KKBP image

FlailingFog commented 2 years ago

I got the fade to be slightly more accurate (the fade should now shrink away from the edges like it does in the game), but I don't know what's going on with the colors. I'm also having a hard time removing that outline on the eyeline because the eyeline texture's alpha and the color don't completely match up, and leveling it doesn't seem to help.

MediaMoots commented 2 years ago

I'm also having a hard time removing that outline on the eyeline because the eyeline texture's alpha and the color don't completely match up, and leveling it doesn't seem to help.

https://blender.stackexchange.com/questions/34626/white-edge-around-objects-with-alpha Does this help?

MediaMoots commented 2 years ago

but I don't know what's going on with the colors.

Looking a bit at the shader code, it seems that a there's a color ramp (or light color), (line 76) but I'm not sure that it's affecting its color that much.

https://github.com/JIexan/Koikatu-shaders-source/blob/master/toon_eyew_lod0.shader

Edit: I can't test now, but the light color is looking a bit sus (maybe the light color of the game is affecting the eyeline) _LightColor0

MediaMoots commented 2 years ago

Figured it out (Invert and Color Burn) KKBP image KK image

FlailingFog commented 2 years ago

@MediaMoots Thanks again for improving the project tenfold!