논리 연산
고친 사람 github-actions[bot]
논리 연산은 참과 거짓을 받아 새로운 참과 거짓을 내는 계산입니다. 「둘 다 참인가」와 「하나라도 참인가」를 묻는 것이 가장 흔합니다. 그래픽스에서는 이 계산으로 화면의 색을 고쳐 쓰는 기능도 같은 이름으로 부릅니다. 새로 그릴 색과 이미 칠해진 색을 비트마다 맞대어 그 결과를 남기는 기능입니다.
쉽고 빠른 이해
논리 연산은 참과 거짓을 따지는 계산입니다. 「로그인했고 관리자인가」처럼 조건 둘을 잇는 것이 논리 연산입니다. 그래픽스는 같은 계산을 색에 씁니다. 화면 한 칸의 색을 0과 1의 줄로 봅니다. 새 색과 있던 색을 한 비트씩 맞댑니다.
색을 합치는 보통 방법은 두 색을 비율로 섞는 것입니다. 그 방법으로는 특정 비트만 골라 켜거나 끄거나 뒤집을 수 없습니다. 비트를 비트로 다루는 길이 따로 있어야 하는 까닭입니다.
어떻게 도나:
- 새로 그릴 색이 가려진 색을 걸러 내는 검사를 통과해 화면에 쓰일 차례가 됩니다
- 그 색과 화면에 있던 색을 비트마다 맞대어 골라 둔 연산을 합니다
- 나온 결과를 화면 메모리에 써넣습니다
대가는 결과가 색으로서 뜻을 잃는다는 것입니다. 두 색 사이의 중간색이 아니라 비트 무늬가 나옵니다. 비율로 섞는 방법과는 한 번에 같이 쓸 수 없습니다. 아예 지원하지 않는 그래픽 환경도 있습니다.
상세
기본 연산과 비트 연산을 먼저 보고 그 위에서 그래픽스의 논리 연산을 블렌딩과 견줍니다.
참과 거짓을 다루는 기본 연산
논리 연산이 다루는 값은 참과 거짓 둘뿐입니다. 컴퓨터는 참을 1, 거짓을 0으로 적습니다. 이렇게 두 값만 갖는 값을 불리언이라고 부릅니다. 조건문이 「맞나 틀리나」를 따지는 일이 전부 이 값 위에서 돕니다.
AND는 둘 다 참일 때만 참을 냅니다. 스위치 둘을 다 올려야 켜지는 전구가 AND 입니다. 「로그인했고 관리자인가」도 AND 입니다.
OR는 하나라도 참이면 참을 냅니다. 스위치 둘 중 하나만 올려도 켜지는 전구가 OR 입니다. NOT은 값 하나를 받아 참과 거짓을 뒤집습니다.
XOR(exclusive OR, 배타적 논리합)는 두 값이 서로 다를 때만 참을 냅니다. 둘 다 참이면 거짓입니다. 「둘 중 하나만 참인가」를 묻는 연산입니다. 뒤에서 그래픽스가 이 성질을 씁니다.
입력 둘이 가질 수 있는 조합은 넷뿐이라 결과를 표 하나에 다 적을 수 있습니다. 이런 표를 진리표라고 부릅니다.
| A | B | A AND B | A OR B | A XOR B |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
맨 아래 줄에서 OR 와 XOR 가 갈립니다. 둘 다 참일 때 OR 는 참이고 XOR 는 거짓입니다.
비트열에 한꺼번에 거는 연산
컴퓨터는 수를 0과 1을 늘어놓은 비트열로 담습니다. 이 0이나 1 하나가 비트입니다. 수 200은 비트 여덟 개로 적으면 11001000 입니다.
논리 연산은 이 비트열 전체에 한 번에 걸 수 있습니다. 두 수의 비트를 같은 위치끼리 짝지어, 짝마다 따로 논리 연산을 합니다. 이것을 비트 연산이라고 부릅니다.
자바나 C 계열 언어에서는 두 기호가 갈립니다. && 는 조건 두 개를 받아 참이나 거짓 하나를 냅니다.
& 는 수 두 개를 받아 비트마다 AND 를 한 새 수를 냅니다. 이 문서의 나머지는 뒤쪽 뜻을 씁니다.
int a = 0b1100;
int b = 0b1010;
a & b // 0b1000 둘 다 1인 비트
a | b // 0b1110 하나라도 1인 비트
a ^ b // 0b0110 서로 다른 비트
세 줄 모두 같은 두 수에서 나왔습니다. 연산만 바꿔도 남는 비트가 달라집니다.
쓰임으로 보면 셋은 역할이 갈립니다. 먼저 건드릴 비트 위치에만 1을 둔 수를 하나 골라 둡니다. 이 수를 비트마스크라고 합니다. 이 수를 셋의 한쪽 입력에 넣습니다.
AND 는 마스크가 1인 비트만 남기고 나머지를 0으로 지웁니다. OR 는 마스크가 1인 비트를 1로 켭니다. XOR 는 마스크가 1인 비트를 뒤집습니다.
그래픽스에서 쓰는 논리 연산
그래픽스의 논리 연산은 새 색을 화면 메모리에 써넣는 마지막 단계에서 고르는 합치기 방법입니다. 두 색을 비율로 섞는 블렌딩 대신 고릅니다. 이 절은 색이 그 단계까지 오는 길을 따라가며 논리 연산이 어디에 끼는지 보입니다.
화면은 픽셀이라는 잘게 나눈 칸으로 이루어집니다. 픽셀마다 색이 하나씩 메모리에 저장됩니다. 그 색도 비트열입니다. 빨강·초록·파랑을 각각 비트 여덟 개로 적는 방식이 흔합니다.
픽셀마다 색을 담아 두는 이 메모리를 색 버퍼라고 합니다. 화면에 보이는 그림은 결국 색 버퍼에 담긴 값입니다.
새로 그린 도형은 픽셀마다 후보 색을 하나씩 냅니다. 이 후보를 색 버퍼에 써넣는 단계가 그리기의 맨 끝에 있는 출력 병합기입니다. 출력 병합기는 후보로 무조건 덮어쓰지 않습니다. 정해 둔 규칙에 따라 있던 색과 합칩니다.
합치기 전에 출력 병합기는 다른 물체에 가려진 후보처럼 그릴 필요가 없는 것을 걸러 냅니다. 이 거르기를 깊이 테스트와 스텐실 테스트가 맡습니다. 걸러진 후보는 버려집니다.
살아남은 후보를 있던 색과 합칠 때 블렌딩과 논리 연산 가운데 하나를 고릅니다. 논리 연산을 고르면 후보 색과 있던 색의 같은 위치 비트끼리 골라 둔 연산을 합니다. 그 결과가 색 버퍼에 남습니다.
flowchart TD
A["후보 색"] --> B
subgraph OM["출력 병합기"]
B["깊이 테스트 · 스텐실 테스트"]
B -->|탈락| X["버린다"]
B -->|통과| C{"합치는 방법 하나를 고른다"}
C --> D["블렌딩 · 비율로 섞는다"]
C --> E["논리 연산 · 비트마다 계산한다"]
end
D --> G["색 버퍼"]
E --> G
그림에서 테두리로 묶은 부분이 출력 병합기입니다. 논리 연산은 블렌딩과 같은 단계에 있습니다. 둘 중 하나만 돕니다. 논리 연산을 켜면 블렌딩은 꺼집니다.
블렌딩과 견주어 보기
차이는 같은 값을 넣어 보면 드러납니다. 있던 색의 빨강 성분이 200, 후보의 빨강 성분이 100 이라고 합시다. 비트로 적으면 11001000 과 01100100 입니다.
| 합치는 방법 | 결과 비트 | 결과 값 |
|---|---|---|
| 블렌딩 · 반씩 섞기 | 10010110 | 150 |
| 논리 연산 · AND | 01000000 | 64 |
| 논리 연산 · OR | 11101100 | 236 |
| 논리 연산 · XOR | 10101100 | 172 |
블렌딩은 두 값 사이의 150 을 냈습니다. 논리 연산 셋은 64, 236, 172 로 흩어집니다. 두 값 사이에 들지도 않습니다. 어느 쪽에 가깝다고 말할 수도 없습니다.
블렌딩은 색을 밝기라는 수로 봅니다. 그래서 결과도 두 색 사이 어딘가의 색입니다. 논리 연산은 색을 비트 무늬로 봅니다. 결과는 밝기로서 뜻이 없습니다. 어느 비트가 켜졌는지만 뜻이 있습니다.
그래서 반투명한 유리나 연기처럼 색이 자연스럽게 겹치는 장면에는 논리 연산을 쓰지 않습니다. 반대로 블렌딩은 값을 곱하고 더하는 계산이라 「셋째 비트만 꺼라」 같은 일을 못 합니다. 비트를 골라 켜고 끄고 뒤집는 일이 논리 연산의 몫입니다.
고를 수 있는 열여섯 가지
그래픽스의 논리 연산은 AND·OR·XOR 셋만 주지 않습니다. 열여섯 가지 가운데 하나를 고르게 합니다. 열여섯은 입력 비트 둘로 만들 수 있는 연산의 전부입니다.
입력 비트 둘의 조합은 넷입니다. 조합마다 결과를 0이나 1로 정할 수 있으니, 정하는 방법은 2를 네 번 곱한 16 가지입니다. 앞의 진리표에서 결과 열 하나를 0과 1로 채우는 방법의 수와 같습니다.
대표 다섯만 보면 이렇습니다. 앞의 넷은 두 입력 가운데 하나나 둘 다를 무시합니다. 마지막 XOR 는 두 입력을 다 씁니다.
| 연산 | 결과 |
|---|---|
| 지우기 | 두 색과 상관없이 언제나 0 |
| 복사 | 후보 색 |
| 아무것도 안 함 | 있던 색. 색 버퍼가 바뀌지 않는다 |
| 뒤집기 | 있던 색의 비트를 전부 뒤집은 값 |
| XOR | 후보와 있던 색이 다른 비트만 1 |
「복사」는 후보 색을 손대지 않고 써넣으니 블렌딩 없이 덮어쓴 것과 결과가 같습니다.
나머지 열한 가지는 AND·OR 와, 지금까지 본 연산에 NOT 을 붙인 것입니다. 예를 들어 AND 결과를 뒤집으면 NAND(Not AND)입니다. OR 결과를 뒤집으면 NOR(Not OR)입니다. 지우기 결과를 뒤집으면 언제나 1을 쓰는 연산이 됩니다.
두 번 그리면 지워지는 XOR
XOR 에는 같은 값을 두 번 걸면 처음으로 돌아오는 성질이 있습니다. 첫 번째 XOR 로 뒤집힌 비트가 두 번째 XOR 에서 다시 뒤집히기 때문입니다.
int bg = 0b11001000; // 있던 색 200
int pen = 0b11111111; // 그릴 색 255
bg ^ pen // 0b00110111 = 55
(bg ^ pen) ^ pen // 0b11001000 = 200
첫 XOR 로 200 이 55 가 됐습니다. 같은 255 를 한 번 더 걸자 200 으로 돌아왔습니다.
이 성질로 화면 위에 잠깐 떠 있는 선을 그릴 수 있습니다. 마우스로 끄는 선택 상자가 그런 예입니다. 상자를 XOR 로 그립니다. 마우스가 움직이면 같은 상자를 같은 위치에 XOR 로 한 번 더 그립니다. 그러면 상자가 지워지고 밑의 그림이 돌아옵니다.
덕분에 상자 밑에 있던 그림을 따로 저장해 둘 필요가 없습니다. 모든 비트가 1인 255 로 그리면 밑의 색이 무엇이든 뒤집힌 색이 나옵니다. 그래서 어떤 배경 위에서도 상자가 눈에 띕니다.
대가와 지원 범위
논리 연산의 결과는 밝기로서 뜻이 없습니다. 화면에 어떤 색이 나올지 눈으로 짐작하기 어렵습니다. XOR 로 그린 상자도 밑의 그림에 따라 픽셀마다 색이 제각각으로 나옵니다.
블렌딩과 같이 못 쓴다는 것도 대가입니다. 반투명하게 섞으면서 비트를 뒤집는 일은 한 번의 그리기로 할 수 없습니다.
지원 범위도 고르지 않습니다. 프로그램이 그래픽 하드웨어에 일을 시키는 창구를 그래픽스 API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)라고 부릅니다. 그래픽스 API 가운데는 논리 연산을 아예 빼 둔 것이 있습니다. 선택 기능으로 두어 하드웨어에 따라 못 쓰는 것도 있습니다.
색 버퍼가 색을 부동소수점 수로 담고 있으면 논리 연산이 걸리지 않는 것이 보통입니다. 부동소수점 수는 비트를 몇 구역으로 나눠 구역마다 다른 뜻을 담습니다. 그 비트를 맞대도 뜻 있는 색이 나오지 않습니다. 그래서 비트를 맞대는 계산은 색을 정수로 담는 버퍼를 전제로 합니다.
관련 항목
논리 연산의 하위 종류
AND · OR · NOT · XOR · NAND · NOR
논리 연산이 비롯된 이론
불 대수 · 불리언 · 진리표 · 드모르간 법칙 · 논리 게이트
논리 연산을 비트열에 거는 연산
비트 · 비트열 · 비트 연산 · 비트마스크 · 비트 시프트 · 2진수
코드에서 논리 연산을 적는 연산자
논리 연산자 · 단락 평가 · 연산자 우선순위 · 조건문
그래픽스에서 논리 연산이 거치는 처리 단계
출력 병합기 · 깊이 테스트 · 스텐실 테스트 · 컬러 라이트 마스크 · 색 버퍼 · 프레임버퍼 · 렌더 타깃 · 그래픽스 파이프라인
논리 연산 대신 색을 합치는 수단
블렌딩 · 알파 채널 · 알파 합성 · 프래그먼트 셰이더
논리 연산을 설정하는 그래픽스 API
OpenGL · Vulkan · Direct3D 11
다른 이름: logical operation · logic operation · logic op · 로직 연산