【秘辛】白色改變顏色
將白色用『圖像效果顏色改變 200』不會變回白色
這是一位老師發現的,將貓咪用『圖像效果顏色改變 200』 之後,貓咪的身體變回原來的顏色,但貓咪嘴巴卻不是白色,而是淡淡的紅色。自己畫一個白色的方形,也把它的顏色改變 200,也是一樣的結果。
這是怎麼回事?將白色改變顏色 200 為什麼不會變回白色,是 Scratch 的 bug 嗎?這引起了傑夫老師的好奇心,決定來找出答案。
實驗步驟
實驗步驟如上面所顯示,顏色是用偵測積木《碰到顏色》來檢查。貓咪身體的顏色,在執行改變顏色的前後都是一樣(顏色 10、彩度 90、亮度 100),但貓咪的嘴巴改變前是純白色(顏色 0、彩度 0、亮度 100),之後卻變成了淡紅色(顏色 0、彩度 9、亮度 100)。原本以為,貓咪嘴巴可能不是純白色,所以另外畫了一個白色底的正方形,重複一樣的步驟,卻也是得到一樣的淡紅色。
把這問題在臉書社團 Teaching with Scratch 中提問,希望大神 RokCoder 會出面解釋,可惜沒回... 只好自己找答案了
臉書社團上的回覆
果然,谷哥大神什麼都知道,經過了一番搜索,最後在 Scratch Discussion 的這一篇中找到答案
» Bugs and Glitches » Grey changes with the color effect https://scratch.mit.edu/discuss/topic/6959/
樓主的問題如下,這是個 2013 年就提出的問題,而且看似與我的問題無關。
樓主的問題
但事隔多年之後,有個回應雖是在討論 Scratch 2.0 與 3.0 的不同,但提到了一樣貓咪的狀況,並請注意這兩句:
If you color-pick it after you'll see that the saturation has gone up… in fact, it's changed from 0 from 9 the moment the color effect isn't at 0.
After some testing, this only happens when the saturation is below 9.
譯:
如果你(改變顏色)之後用顏色吸管吸取顏色,會發現飽和度(彩度)被提高了...具體的說,一旦做了顏色不為0的改變,飽和度(彩度)便從0變成了9。
經過一些測試,這種狀況只發生在飽和度(彩度)小於9的時候。
多年之後,有個回應提到了一樣的狀況
這篇討論繼續往下看,就看到了真相: Yes, it's intentional. (譯:對,這是故意的)
真相: Yes, it's intentional.
故意的!?瞎毀,然後就一段程式。不過幸運的是這段程式有寫註解,大意是:
這段程式強迫把灰階值變成稍微飽和,這樣些許的色彩改變才看得出來
// this code forces grayscale values to be slightly saturated
// so that some slight change of hue will be visible
const float minLightness = 0.11 / 2.0;
const float minSaturation = 0.09;
if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);
else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);
遠遠聽到有人在說:傑夫老師有翻譯跟沒翻譯不都一樣,看不懂呀!
傑夫老師會回說,有程式不會自己看呀~
對~我們來看程式,下面這段是 github 上最新的程式,程式雖然已經被修改了,但功能沒改變
https://github.com/LLK/scratch-render/blob/develop/src/EffectTransform.js
這段程式在函式 transformColor (drawable, inOutColor, effectMask) 中,也就是要做顏色改變時會執行到的函式。
const hsv = rgbToHsv(inOutColor, __hsv);
// this code forces grayscale values to be slightly saturated
// so that some slight change of hue will be visible
// const float minLightness = 0.11 / 2.0;
const minV = 0.11 / 2.0;
// const float minSaturation = 0.09;
const minS = 0.09;
// if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);
if (hsv[2] < minV) {
hsv[0] = 0;
hsv[1] = 1;
hsv[2] = minV;
// else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);
} else if (hsv[1] < minS) {
hsv[0] = 0;
hsv[1] = minS;
}
在看之前,先要知道 Scratch 的顏色其實是可以對應到 HSV 的。不清楚 HSV 的請參考 wikipedia HSL和HSV色彩空間
顏色 對應到 Hue (色調) 程式中的 hsv[0]
彩度 對應到 Saturation (飽和) 程式中的 hsv[1]
亮度 對應到 Lightness (明度、亮度) 程式中的 hsv[2]
hsv[0], [1], [2] 這三個值再從 rgbToHsv 的註解中知道範圍是從 0~1
那來看 else if 這段程式:
} else if (hsv[1] < minS) { //檢查要改變的顏色的彩度,如果小於minS(0.09)
hsv[0] = 0; //就把顏色設為0
hsv[1] = minS; //再把彩度設為0.09
}
白色的三個值都是 0,所以這個判斷會成立,那彩度 hsv[1] 就會被改成 0.09。剛剛說了程式中 hsv 值的範圍都是 0~1,那麼 0.09 算回 Scratch 的彩度就是 9。
好了,真相大白,只要顏色的彩度小於 9,當執行『圖像效果顏色改變』時,彩度就會被強迫設為 9,而我們看到的白色顏色改變就是這符合這個狀況。換句話說,全白的顏色因為沒有任何彩度,對它做任的何顏色改變,也感覺不到有變顏色,所以程式中將彩度調高一點,才能讓顏色的改變顯現出來。這句話其實就是前面那段程式註解要說明的事。
別走!事情還沒結束,程式還有一段,來看看什麼意思:
if (hsv[2] < minV) { //如果要改變的顏色的亮度小於minV(0.055)
hsv[0] = 0; //就把顏色設為0
hsv[1] = 1; //再把彩度設為1
hsv[2] = minV; //最後把亮度設為0.055
}
這段程式的目的,就是要把亮度太低的顏色,稍微調高亮度並把彩度調到最高,這樣改變顏色後才能看出顏色。不過,由於亮度還是太低,傑夫老師用色域較廣的螢幕並把螢幕亮度打到最亮,顏色的變化也沒有很明顯,還是很接近黑色。
有興趣研究的朋友,可以把一個黑色(顏色 0、彩度 0、亮度 0)的角色用『圖像效果顏色改變 200』改變顏色之後,就會發現顏色還是接近黑色,但其實已經變成了(顏色 0、彩度 100、亮度 5)喔~
好啦~這個 Scratch 鮮為人知的秘辛就說明到這邊,下次再跟大家分享。Bye now~