There is no simple physical model that will do this; the painter's colors have very complex interactions with light. Fortunately, we have computers that are not limited to modeling the physical world - we can get them to do any arbitrary thing that we need!
The first step is to create a color wheel with the distribution of shades that we need, with red, yellow and blue in increments of 120 degrees. There are many examples on the Internet. I created one here that has only saturated colors, so it can be used to create a full RGB gamut. The colors on the wheel are completely arbitrary; I set orange (60 °) to (255,160,0) because the midpoint between red and yellow was too red, and I moved pure blue (from 0.0255) to 250 ° instead of 240 °, so that 240 ° Blue would be look better.

Remembering the experiments of my childhood, when you mix the same amount of red, yellow and blue together, you get a fuzzy brownish-gray color. I have chosen a suitable color that you can see in the center of the color wheel; in the code, I affectionately call it "dirt."
To get every conceivable color that you need more than red, yellow and blue, you also need to mix white and black. For example, you get Pink by mixing red and white, and you get Brown by mixing orange (yellow + red) with black.
Conversion works with relationships, not absolute numbers. As with real paint, there is no difference between mixing 1 part red and 1 part yellow, versus 100 parts red and 100 parts yellow.
The code is presented in Python, but it is difficult to convert to other languages. The hardest part is adding red, yellow, and blue to create a hue angle. I use vector addition and convert back to corner with atan2 . Almost everything else is done with linear interpolation (lerp).
# elementary_colors.py from math import degrees, radians, atan2, sin, cos red = (255, 0, 0) orange = (255, 160, 0) yellow = (255, 255, 0) green = (0, 255, 0) cyan = (0, 255, 255) blue = (0, 0, 255) magenta = (255, 0, 255) white = (255, 255, 255) black = (0, 0, 0) mud = (94, 81, 74) colorwheel = [(0, red), (60, orange), (120, yellow), (180, green), (215, cyan), (250, blue), (330, magenta), (360, red)] red_x, red_y = cos(radians(0)), sin(radians(0)) yellow_x, yellow_y = cos(radians(120)), sin(radians(120)) blue_x, blue_y = cos(radians(240)), sin(radians(240)) def lerp(left, right, left_part, total): if total == 0: return left ratio = float(left_part) / total return [l * ratio + r * (1.0 - ratio) for l,r in zip(left, right)] def hue_to_rgb(deg): deg = deg % 360 previous_angle, previous_color = colorwheel[0] for angle, color in colorwheel: if deg <= angle: return lerp(previous_color, color, angle - deg, angle - previous_angle) previous_angle = angle previous_color = color def int_rgb(rgb): return tuple(int(c * 255.99 / 255) for c in rgb) def rybwk_to_rgb(r, y, b, w, k): if r == 0 and y == 0 and b == 0: rgb = white else: hue = degrees(atan2(r * red_y + y * yellow_y + b * blue_y, r * red_x + y * yellow_x + b * blue_x)) rgb = hue_to_rgb(hue) rgb = lerp(mud, rgb, min(r, y, b), max(r, y, b)) gray = lerp(white, black, w, w+k) rgb = lerp(rgb, gray, r+y+b, r+y+b+w+k) return int_rgb(rgb)