Search code examples
wpfcolorsmixing

Color mixing results depend on Z order of elements?


Well, normally, if I mix 2 colors, mathematically my expectation is: `(color1+color2)/2.
So green/2+red/2=yellow (255,0,0)/2+(0,255,0)/2=(127,127,0)`.


I prepared test in WPF. And I got result which I hardly can explain.

So what do you think? Why and how does result depend on z-order?

PS: I don't have reputation here, so, my apologies - cannot share with you some pictures, which makes subject clear. Please spend some time and paste my code into your project to see what I am talking here about

PPS: here is code to visualize color mixing problem in WPF

<Grid>
    <Grid.Resources>
        <Color A='100' R='0' G='0' B='255' x:Key='clr01' />
        <Color A='100' R='0' G='255' B='0' x:Key='clr02' />
        <Color A='100' R='255' G='0' B='0' x:Key='clr03' />

        <SolidColorBrush Color='{StaticResource clr01}' x:Key='scb01' />
        <SolidColorBrush Color='{StaticResource clr02}' x:Key='scb02' />
        <SolidColorBrush Color='{StaticResource clr03}' x:Key='scb03' />
    </Grid.Resources>
    <TabControl >
        <TabItem Header='case 1 rectangles (actual)'>
            <Grid>
                <Grid.Resources>
                    <Style TargetType='{x:Type Border}'>
                        <Setter Property='BorderBrush' Value='Black' />
                        <Setter Property='BorderThickness' Value='1' />
                        <Setter Property='SnapsToDevicePixels' Value='True' />
                        <Setter Property='Height' Value='20' />
                        <Setter Property='Width' Value='400' />
                    </Style>
                </Grid.Resources>
                <Canvas>
                    <Border Background='{StaticResource scb01}' Canvas.Left="10" Canvas.Top="10" />
                    <Border Background='{StaticResource scb02}' Canvas.Left="100" Canvas.Top="10" />

                    <Border Background='{StaticResource scb02}' Canvas.Left="100" Canvas.Top="40" />
                    <Border Background='{StaticResource scb01}' Canvas.Left="10" Canvas.Top="40" />



                    <Border Background='{StaticResource scb02}' Canvas.Left="10" Canvas.Top="90" />
                    <Border Background='{StaticResource scb03}' Canvas.Left="100" Canvas.Top="90" />

                    <Border Background='{StaticResource scb03}' Canvas.Left="100" Canvas.Top="120" />
                    <Border Background='{StaticResource scb02}' Canvas.Left="10" Canvas.Top="120" />



                    <Border Background='{StaticResource scb03}' Canvas.Left="10" Canvas.Top="170" />
                    <Border Background='{StaticResource scb01}' Canvas.Left="100" Canvas.Top="170" />

                    <Border Background='{StaticResource scb01}' Canvas.Left="100" Canvas.Top="200" />
                    <Border Background='{StaticResource scb03}' Canvas.Left="10" Canvas.Top="200" />
                </Canvas>
            </Grid>
        </TabItem>
        <TabItem Header='case 2 circles'>
            <Grid >
                <Grid.Resources>
                    <Style TargetType='{x:Type Ellipse}'>
                        <Setter Property='Stroke' Value='Black' />
                        <Setter Property='StrokeThickness' Value='1' />
                        <Setter Property='SnapsToDevicePixels' Value='True' />
                        <Setter Property='Width' Value='100' />
                        <Setter Property='Height' Value='100' />
                    </Style>
                    <Style TargetType='{x:Type Border}'>
                        <Setter Property='BorderBrush' Value='Black' />
                        <Setter Property='BorderThickness' Value='1' />
                        <Setter Property='SnapsToDevicePixels' Value='True' />
                        <Setter Property='Height' Value='250' />
                        <Setter Property='Width' Value='250' />
                    </Style>
                </Grid.Resources>
                <Canvas Background='White'>
                    <Border Canvas.Left="10" Canvas.Top="10">
                        <Canvas >
                            <Ellipse Canvas.Left="50" Canvas.Top="50"  Fill='{StaticResource scb01}' />
                            <Ellipse Canvas.Left="100" Canvas.Top="50" Fill='{StaticResource scb02}' />
                            <Ellipse Canvas.Left="75" Canvas.Top="100" Fill='{StaticResource scb03}' />
                        </Canvas>
                    </Border>
                    <Border Canvas.Left="270" Canvas.Top="10">
                        <Canvas >
                            <Ellipse Canvas.Left="75" Canvas.Top="100" Fill='{StaticResource scb03}' />
                            <Ellipse Canvas.Left="50" Canvas.Top="50"  Fill='{StaticResource scb01}' />
                            <Ellipse Canvas.Left="100" Canvas.Top="50" Fill='{StaticResource scb02}' />
                        </Canvas>
                    </Border>
                    <Border Canvas.Left="530" Canvas.Top="10">
                        <Canvas >
                            <Ellipse Canvas.Left="100" Canvas.Top="50" Fill='{StaticResource scb02}' />
                            <Ellipse Canvas.Left="75" Canvas.Top="100" Fill='{StaticResource scb03}' />
                            <Ellipse Canvas.Left="50" Canvas.Top="50"  Fill='{StaticResource scb01}' />
                        </Canvas>
                    </Border>
                </Canvas>
            </Grid>
        </TabItem>
    </TabControl>
</Grid>

Update: user2864740, thanks for the advice, here is the link: Project in ZIP and 3 screenshots with problem visualization


Solution

  • Well when you combine colors with transparency you should apply the equation:

    result = alpha * srcColor + (1 - alpha) * dstColor
    

    Where srcColor is the color of the element on top and dstColor the color of what is under. In your case the alpha is 100 which is approximatly 40%. So your formula don't work.

    The other thing to consider is the white background of the window. Actually you have to calculate this equation two times.

    Lets try to calculate.

    white + transparent red + transparent green ->

    0.4 * red + 0.6 * white = (1, 0.6, 0.6)
    0.4 * green + 0.6 * (1, 0.6, 0.6) = (0.6, 0.76, 0.36)
    

    white + transparent green + transparent red ->

    0.4 * green + 0.6 * white = (0.6, 1, 0.6)
    0.4 * red + 0.6 * (1, 0.6, 0.6) = (0.76, 0.6, 0.36)
    

    The result depends on order and that's why it is so difficult to implement high performance transparency in 3D. In 2D you just sort with the Z order to get desired results.