Creating complex alpha masks in real-time?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
BunnyInfernal
Regular
Posts: 34
Joined: Wed Dec 26, 2018 10:45 am
Contact:

Creating complex alpha masks in real-time?

#1 Post by BunnyInfernal » Tue Aug 16, 2022 1:31 pm

Creating complex alpha masks in real-time?

Ren’Py has advanced a lot since I last tackled this, and I was hoping someone here might be able to put me on the path towards a reasonably optimized way of doing this:

I’ve been using a paper-doll system for the player character in my game where the character tans based on the swimsuit(s) they’ve been wearing.


So far, to accomplish the effect, I basically have two main images of the character—one pale and one with maximum tan. The tanned image is laid on top of the pale one with an alpha mask to essentially “apply” the tan effect.

So far, my biggest problem is how to create this alpha mask in-game.

I have a convoluted method that’s been working, but it’s probably a performance nightmare, so I was hopping someone could suggest a method that might be more appropriate.

Currently I have 3 grayscale images/alpha masks to represent each of the tanline possibilities (technically 2, as the 3rd is just a solid color).

In-game, there is a value of 0-100 representing how much each of each type of tan the player character has (essentially an alpha value of 0.0 - 1.0). Those three variables are updated on a regular basis throughout the game.

What I’d like to be able to do is layer those three images on top of each other (at their current opacity levels) and create a single alpha mask that will be applied to the tan image. It has to be able to update on a regular basis as the values change.

(Technically, there will be more than one of these, as I’ll need a set for each image of the character.)

Any thoughts/suggestions on efficient ways of pulling this off?

User avatar
m_from_space
Veteran
Posts: 302
Joined: Sun Feb 21, 2021 3:36 am
Contact:

Re: Creating complex alpha masks in real-time?

#2 Post by m_from_space » Wed Aug 17, 2022 12:58 am

How about you share your code, so we can see what method you are using? Do you experience a performance issue?

Sounds like you want to create a DynamicDisplayable using a Composite image and AlphaMask.

https://www.renpy.org/doc/html/displayables.html

Something I would do, but I have no clue how your images really apply here:

Code: Select all

default tan_amount = 0.0

init python:

    def tannedImage(st, at):
        global tan_amount
        
        # load a base image
        img_base = Image(PATH_TO_BASE_IMAGE)
        
        # load a second image, applying an opacity
        img_tan = im.MatrixColor(PATH_TO_TAN_IMAGE, im.matrix.opacity(tan_amount))
        
        # apply alpha mask to tan image
        img_tan = AlphaMask(img_tan, PATH_TO_MASK)

        # put both images together
        img = Composite((IMG_WIDTH, IMG_HEIGHT), (0,0), img_base, (0,0), img_tan)
        
        # return the image, None means that we won't call the function until the next interaction
        return img, None

image tannedperson = DynamicDisplayable(tannedImage)

label start:
    show tannedperson
    "Look, I have no tan. Let's change that..."
    $ tan_amount = 0.5
    "That was fast."
edit: Changed order of some lines, since you seem to want to apply the alphamask to the tanned image, not the final one. I'm not sure you realize what AlphaMask means, maybe you only mean opacity. Let me know.

BunnyInfernal
Regular
Posts: 34
Joined: Wed Dec 26, 2018 10:45 am
Contact:

Re: Creating complex alpha masks in real-time?

#3 Post by BunnyInfernal » Wed Aug 17, 2022 2:20 pm

I know what I'm trying to do is difficult to convey in text. I've got some images that I help will clear it up a bit more.

I'm starting with the pale and tan images:

Start Images.jpg

I've got three mask images that I'm using for the tan-lines (though the third is technically just a solid color):

3 masks.jpg

I'm setting an opacity level for each of those three images separately (values from 0-100), based on in-game stats. To get the final mask that will be used on the tan image, I merge those three together into a combined mask:

Tan Masks.jpg

I'll have to go through the game code later to see if I can find the relevant sections from my working version. It's really convoluted, as there's far more to it than just the tan section (there are sections for hair and clothing, etc.).

User avatar
m_from_space
Veteran
Posts: 302
Joined: Sun Feb 21, 2021 3:36 am
Contact:

Re: Creating complex alpha masks in real-time?

#4 Post by m_from_space » Thu Aug 18, 2022 3:20 am

Okay that's helpful. And it's possible with the suggestion I gave you. I would try the following:

Code: Select all

# renpy will normally load those images automatically, and you can use their file names, but let's do it manually for clarity
image body_pale = "/images/..."
image body_tan = "/images/..."
image mask_one = ...
image mask_two = ...
image mask_nosuit = ...

# your opacity levels (0 to 100), better would be 0.0 to 1.0 to not have to divide by 100 later on
default mask_one_value = 0
default mask_two_value = 0
default mask_nosuit_value = 0

init python:

    def tannedImage(st, at):

        # apply current opacity levels to masks (maybe there is a better way to apply opacity I don't know about)
        img_one = im.MatrixColor(mask_one, im.matrix.opacity(mask_one_value/100.0))
        img_two = im.MatrixColor(mask_two, im.matrix.opacity(mask_two_value/100.0))
        img_nosuit = im.MatrixColor(mask_nosuit, im.matrix.opacity(mask_nosuit_value/100.0))

        # combine the masks
        img_mask = Composite((IMG_WIDTH, IMG_HEIGHT), (0,0), img_one, (0,0), img_two, (0,0), img_nosuit)

        # apply mask to tanned body
        img_tan = AlphaMask(body_tan, img_mask)

        # put tanned parts on top of pale body
        img_final = Composite((IMG_WIDTH, IMG_HEIGHT), (0,0), body_pale, (0,0), img_tan)

        # return the image
        return img_final, None

image tannedperson = DynamicDisplayable(tannedImage)

label start:
    show tannedperson
    "Look, I have no tan. Let's change that..."
    $ mask_one_value = 50
    "That was fast."

BunnyInfernal
Regular
Posts: 34
Joined: Wed Dec 26, 2018 10:45 am
Contact:

Re: Creating complex alpha masks in real-time?

#5 Post by BunnyInfernal » Thu Aug 18, 2022 9:35 am

m_from_space wrote:
Thu Aug 18, 2022 3:20 am
Okay that's helpful. And it's possible with the suggestion I gave you. I would try the following:
Thanks for the suggestion! I'm going to have to spend a little time with it and see how it works for me.

m_from_space wrote:
Thu Aug 18, 2022 3:20 am

Code: Select all

# apply current opacity levels to masks (maybe there is a better way to apply opacity I don't know about)
        img_one = im.MatrixColor(mask_one, im.matrix.opacity(mask_one_value/100.0))
Yes, I was wondering about the im.MatrixColor part, too, as I'm pretty sure that's what I've been using in places.

I wasn't sure if Renpy has a more efficient way of handling oppacity now, or if that's still the best method.

BunnyInfernal
Regular
Posts: 34
Joined: Wed Dec 26, 2018 10:45 am
Contact:

Re: Creating complex alpha masks in real-time?

#6 Post by BunnyInfernal » Sat Aug 20, 2022 10:00 am

I tried it like this:

Code: Select all

# renpy will normally load those images automatically, and you can use their file names, but let's do it manually for clarity
image body_pale = "images/avatar/pale.webp"
image body_tan = "images/avatar/tanned.webp"
image mask_one = "images/avatar/1p_msk.webp"
image mask_two = "images/avatar/2p_msk.webp"
image mask_nosuit = "images/avatar/no_msk.webp"


# your opacity levels (0 to 100), better would be 0.0 to 1.0 to not have to divide by 100 later on
default mask_one_value = 0
default mask_two_value = 0
default mask_nosuit_value = 0

init python:

    def tannedImage(st, at):

        # apply current opacity levels to masks (maybe there is a better way to apply opacity I don't know about)
        img_one = im.MatrixColor(mask_one, im.matrix.opacity(mask_one_value/100.0))
        img_two = im.MatrixColor(mask_two, im.matrix.opacity(mask_two_value/100.0))
        img_nosuit = im.MatrixColor(mask_nosuit, im.matrix.opacity(mask_nosuit_value/100.0))

        # combine the masks
        img_mask = Composite((909, 720), (0,0), img_one, (0,0), img_two, (0,0), img_nosuit)

        # apply mask to tanned body
        img_tan = AlphaMask(body_tan, img_mask)

        # put tanned parts on top of pale body
        img_final = Composite((909, 720), (0,0), body_pale, (0,0), img_tan)

        # return the image
        return img_final, None

image tannedperson = DynamicDisplayable(tannedImage)
But I get this error:

Code: Select all

I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/script.rpy", line 19, in script
    show tannedperson
  File "game/systems/avatar_x/avatar_x2.rpy", line 19, in tannedImage
    img_one = im.MatrixColor(mask_one, im.matrix.opacity(mask_one_value/100.0))
NameError: name 'mask_one' is not defined
If I change the Images to string variables...

Code: Select all

default body_pale = "images/avatar/pale.webp"
default body_tan = "images/avatar/tanned.webp"
default mask_one = "images/avatar/1p_msk.webp"
default mask_two = "images/avatar/2p_msk.webp"
default mask_nosuit = "images/avatar/no_msk.webp"
...then it works. I think you can't pass a displayable to an image modifier?

Post Reply

Who is online

Users browsing this forum: No registered users